@vrs-soft/wecom-aibot-mcp 2.4.3 → 2.4.4

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.js CHANGED
@@ -313,9 +313,15 @@ async function main() {
313
313
  // 确定安装模式
314
314
  const installMode = args.includes('--http-only') ? 'http-only' :
315
315
  args.includes('--channel-only') ? 'channel-only' : 'full';
316
- // --reinstall / --http-only / --setup 命令跳过顶部 ensureGlobalConfigs
317
- // (--setup 自己在向导完成后调用)
318
- if (!args.includes('--reinstall') && !args.includes('--http-only') && !args.includes('--setup')) {
316
+ // 以下命令跳过顶部 ensureGlobalConfigs,避免覆盖配置
317
+ // --setup: 向导完成后自己调用
318
+ // --channel: 作为 Channel MCP 代理运行,不应改写全局配置
319
+ // --reinstall / --http-only: 有自己的处理逻辑
320
+ // --version / -v: 只查版本,不写配置
321
+ const skipEnsure = args.includes('--reinstall') || args.includes('--http-only') ||
322
+ args.includes('--setup') || args.includes('--channel') ||
323
+ args.includes('--version') || args.includes('-v');
324
+ if (!skipEnsure) {
319
325
  // 强制覆盖所有全局配置(不依赖智能体)
320
326
  ensureGlobalConfigs(installMode);
321
327
  }
@@ -516,29 +522,45 @@ async function main() {
516
522
  setAuthToken(token);
517
523
  // HTTPS 证书配置
518
524
  const defaultCertPath = path.join(os.homedir(), '.wecom-aibot-mcp', 'cert.pem');
519
- const defaultKeyPath = path.join(os.homedir(), '.wecom-aibot-mcp', 'key.pem');
520
525
  console.log('\n HTTPS 证书配置(留空跳过,保持 HTTP 模式)');
521
- console.log(' 默认证书位置(将证书文件放到此路径即可):');
522
- console.log(` 证书: ${defaultCertPath}`);
523
- console.log(` 私钥: ${defaultKeyPath}`);
524
- console.log(' 如使用 Let\'s Encrypt,路径通常为:');
525
- console.log(' /etc/letsencrypt/live/<域名>/fullchain.pem');
526
- console.log(' /etc/letsencrypt/live/<域名>/privkey.pem\n');
526
+ console.log(' 请输入完整路径含文件名(.pem / .crt / .key 均可),例如:');
527
+ console.log(` ${defaultCertPath}`);
528
+ console.log(' /etc/letsencrypt/live/example.com/fullchain.pem');
529
+ console.log(' /etc/gitlab/ssl/gitlab.example.com.crt\n');
530
+ const checkFile = (p, label) => {
531
+ if (!fs.existsSync(p)) {
532
+ console.log(`[setup] ⚠️ ${label}文件不存在: ${p}`);
533
+ return false;
534
+ }
535
+ if (fs.statSync(p).isDirectory()) {
536
+ console.log(`[setup] ⚠️ ${label}路径是目录而非文件: ${p}`);
537
+ return false;
538
+ }
539
+ return true;
540
+ };
527
541
  const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
528
- const certInput = await new Promise(resolve => rl2.question(`SSL 证书路径(直接回车使用默认): `, a => { rl2.close(); resolve(a.trim()); }));
529
- const certPath = certInput || defaultCertPath;
530
- if (fs.existsSync(certPath)) {
531
- const rl3 = readline.createInterface({ input: process.stdin, output: process.stdout });
532
- const keyInput = await new Promise(resolve => rl3.question(`SSL 私钥路径(直接回车使用默认): `, a => { rl3.close(); resolve(a.trim()); }));
533
- const keyPath = keyInput || defaultKeyPath;
534
- setHttpsConfig(certPath, keyPath);
535
- console.log(`[setup] HTTPS 已配置: ${certPath}`);
536
- }
537
- else if (certInput) {
538
- console.log(`[setup] ⚠️ 证书文件不存在: ${certPath},跳过 HTTPS 配置`);
542
+ const certInput = await new Promise(resolve => rl2.question(`SSL 证书文件完整路径(留空跳过): `, a => { rl2.close(); resolve(a.trim()); }));
543
+ if (certInput) {
544
+ if (!checkFile(certInput, '证书')) {
545
+ console.log('[setup] 跳过 HTTPS 配置');
546
+ }
547
+ else {
548
+ const rl3 = readline.createInterface({ input: process.stdin, output: process.stdout });
549
+ const keyInput = await new Promise(resolve => rl3.question(`SSL 私钥文件完整路径: `, a => { rl3.close(); resolve(a.trim()); }));
550
+ if (keyInput && checkFile(keyInput, '私钥')) {
551
+ setHttpsConfig(certInput, keyInput);
552
+ console.log(`[setup] HTTPS 已配置`);
553
+ console.log(` 证书: ${certInput}`);
554
+ console.log(` 私钥: ${keyInput}`);
555
+ }
556
+ else if (!keyInput) {
557
+ console.log('[setup] 私钥路径不能为空,跳过 HTTPS 配置');
558
+ }
559
+ }
539
560
  }
540
561
  else {
541
- console.log(`[setup] 将证书放到默认位置后重新运行 --setup --server 即可启用 HTTPS`);
562
+ console.log(`[setup] 跳过 HTTPS,使用 HTTP 模式`);
563
+ console.log(`[setup] 如需启用 HTTPS,配置证书后重新运行 --setup --server`);
542
564
  }
543
565
  console.log('\n[setup] Server 配置完成!');
544
566
  console.log(' 启动: npx @vrs-soft/wecom-aibot-mcp --http-only --start');
@@ -548,23 +570,31 @@ async function main() {
548
570
  else if (wantChannel) {
549
571
  // Channel 客户端
550
572
  console.log('\n[setup] Channel Client 安装模式\n');
551
- let mcpUrl = process.env.MCP_URL;
573
+ // 交互式安装必须每次都提示,不能直接用已有的环境变量(可能是旧值)
574
+ const readline = await import('readline');
575
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
576
+ const existingUrl = process.env.MCP_URL || '';
577
+ const urlPrompt = existingUrl
578
+ ? `远程服务器地址(当前: ${existingUrl},直接回车保持不变): `
579
+ : `远程服务器地址(如 https://your-server:18963): `;
580
+ const urlInput = await new Promise(resolve => rl.question(urlPrompt, a => { rl.close(); resolve(a.trim()); }));
581
+ const mcpUrl = urlInput || existingUrl;
552
582
  if (!mcpUrl) {
553
- const readline = await import('readline');
554
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
555
- mcpUrl = await new Promise(resolve => rl.question('远程服务器地址(如 https://your-server:18963): ', a => { rl.close(); resolve(a.trim()); }));
556
- if (!mcpUrl) {
557
- console.log('[setup] ❌ 地址不能为空');
558
- process.exit(1);
559
- }
560
- process.env.MCP_URL = mcpUrl;
583
+ console.log('[setup] 地址不能为空');
584
+ process.exit(1);
561
585
  }
562
- if (!getAuthToken()) {
563
- const readline = await import('readline');
564
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
565
- const token = await new Promise(resolve => rl.question('Auth Token: ', a => { rl.close(); resolve(a.trim()); }));
566
- if (token)
567
- setAuthToken(token);
586
+ process.env.MCP_URL = mcpUrl;
587
+ {
588
+ const existingToken = getAuthToken();
589
+ const tokenPrompt = existingToken
590
+ ? `Auth Token(当前: ${existingToken.slice(0, 8)}...${existingToken.slice(-4)},直接回车保持不变): `
591
+ : 'Auth Token(留空跳过): ';
592
+ const readline2 = await import('readline');
593
+ const rl2 = readline2.createInterface({ input: process.stdin, output: process.stdout });
594
+ const tokenInput = await new Promise(resolve => rl2.question(tokenPrompt, a => { rl2.close(); resolve(a.trim()); }));
595
+ const finalToken = tokenInput || existingToken || '';
596
+ if (finalToken)
597
+ setAuthToken(finalToken);
568
598
  }
569
599
  ensureGlobalConfigs('channel-only');
570
600
  console.log('[setup] Channel MCP 配置完成!请重启 Claude Code 以加载配置');
@@ -1032,9 +1032,10 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
1032
1032
  }
1033
1033
  if (!claudeConfig.mcpServers)
1034
1034
  claudeConfig.mcpServers = {};
1035
+ const mcpEndpointUrl = remoteOptions.url.replace(/\/+$/, '') + '/mcp';
1035
1036
  claudeConfig.mcpServers['wecom-aibot'] = {
1036
1037
  type: 'http',
1037
- url: remoteOptions.url,
1038
+ url: mcpEndpointUrl,
1038
1039
  headers: { Authorization: `Bearer ${remoteOptions.token}` },
1039
1040
  };
1040
1041
  fs.writeFileSync(CLAUDE_CONFIG_FILE, JSON.stringify(claudeConfig, null, 2));
@@ -1044,8 +1045,8 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
1044
1045
  }
1045
1046
  // remote-channel 模式:写入远程 HTTP MCP(带 token)+ Channel MCP
1046
1047
  if (mode === 'remote-channel') {
1047
- if (!remoteOptions?.url || !remoteOptions?.token) {
1048
- console.log('[config] ❌ 远程模式需要提供 URL 和 Token');
1048
+ if (!remoteOptions?.url) {
1049
+ console.log('[config] ❌ 远程模式需要提供 URL');
1049
1050
  return { upgraded: false, previousVersion };
1050
1051
  }
1051
1052
  let claudeConfig = {};
@@ -1054,21 +1055,20 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
1054
1055
  }
1055
1056
  if (!claudeConfig.mcpServers)
1056
1057
  claudeConfig.mcpServers = {};
1057
- // HTTP MCP 配置(带 token
1058
- claudeConfig.mcpServers['wecom-aibot'] = {
1059
- type: 'http',
1060
- url: remoteOptions.url,
1061
- headers: { Authorization: `Bearer ${remoteOptions.token}` },
1062
- };
1058
+ // HTTP MCP 配置(带 token,可选)
1059
+ const mcpEndpointUrl = remoteOptions.url.replace(/\/+$/, '') + '/mcp';
1060
+ const httpMcpConfig = { type: 'http', url: mcpEndpointUrl };
1061
+ if (remoteOptions.token)
1062
+ httpMcpConfig.headers = { Authorization: `Bearer ${remoteOptions.token}` };
1063
+ claudeConfig.mcpServers['wecom-aibot'] = httpMcpConfig;
1063
1064
  // Channel MCP 配置(带 MCP_URL + MCP_AUTH_TOKEN)
1064
- const binPath = path.join(__dirname, 'bin.js');
1065
+ const channelEnvRemote = { MCP_URL: remoteOptions.url.replace(/\/+$/, '') };
1066
+ if (remoteOptions.token)
1067
+ channelEnvRemote.MCP_AUTH_TOKEN = remoteOptions.token;
1065
1068
  claudeConfig.mcpServers['wecom-aibot-channel'] = {
1066
- command: 'node',
1067
- args: [binPath, '--channel'],
1068
- env: {
1069
- MCP_URL: remoteOptions.url,
1070
- MCP_AUTH_TOKEN: remoteOptions.token,
1071
- },
1069
+ command: 'npx',
1070
+ args: ['-y', '@vrs-soft/wecom-aibot-mcp', '--channel'],
1071
+ env: channelEnvRemote,
1072
1072
  };
1073
1073
  fs.writeFileSync(CLAUDE_CONFIG_FILE, JSON.stringify(claudeConfig, null, 2));
1074
1074
  console.log('[config] remote-channel 模式:已写入 HTTP MCP + Channel MCP 配置(带 Token)');
@@ -1086,6 +1086,11 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
1086
1086
  }
1087
1087
  if (!claudeConfig.mcpServers)
1088
1088
  claudeConfig.mcpServers = {};
1089
+ // 从 node_modules 运行(npm/npx 安装)时用 npx,本地开发时用绝对路径
1090
+ const isPackageInstall = __dirname.includes('node_modules');
1091
+ const channelCmd = isPackageInstall
1092
+ ? { command: 'npx', args: ['-y', '@vrs-soft/wecom-aibot-mcp', '--channel'] }
1093
+ : { command: 'node', args: [path.join(__dirname, 'bin.js'), '--channel'] };
1089
1094
  if (mode === 'channel-only') {
1090
1095
  // Channel-only 模式:必须通过 MCP_URL 指定远程地址
1091
1096
  const mcpUrl = process.env.MCP_URL;
@@ -1094,19 +1099,17 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
1094
1099
  console.log('[config] 请设置环境变量: MCP_URL=http://远程IP:18963');
1095
1100
  return { upgraded: false, previousVersion };
1096
1101
  }
1097
- // Channel MCP 配置:使用当前模块路径
1098
- const binPath = path.join(__dirname, 'bin.js');
1099
- const channelEnv = { MCP_URL: mcpUrl };
1102
+ const channelEnv = { MCP_URL: mcpUrl.replace(/\/+$/, '') };
1100
1103
  const authToken = getAuthToken();
1101
1104
  if (authToken) {
1102
1105
  channelEnv.MCP_AUTH_TOKEN = authToken;
1103
1106
  }
1104
1107
  claudeConfig.mcpServers['wecom-aibot-channel'] = {
1105
- command: 'node',
1106
- args: [binPath, '--channel'],
1108
+ command: channelCmd.command,
1109
+ args: channelCmd.args,
1107
1110
  env: channelEnv,
1108
1111
  };
1109
- console.log(`[config] Channel-only 模式:Channel MCP 使用本地路径`);
1112
+ console.log(`[config] Channel-only 模式:Channel MCP 已配置`);
1110
1113
  }
1111
1114
  else {
1112
1115
  // full 模式:同时写入 HTTP MCP 和 Channel MCP 配置
@@ -1114,12 +1117,23 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
1114
1117
  type: 'http',
1115
1118
  url: 'http://127.0.0.1:18963/mcp',
1116
1119
  };
1117
- // Channel MCP 配置:使用当前模块路径
1118
- const binPath = path.join(__dirname, 'bin.js');
1120
+ // Channel MCP 配置:保留已有的自定义 MCP_URL(如 channel-only 安装时写入的远程地址)
1121
+ const existingChannel = claudeConfig.mcpServers['wecom-aibot-channel'];
1122
+ const existingMcpUrl = existingChannel?.env?.MCP_URL;
1123
+ const isRemote = existingMcpUrl && !existingMcpUrl.startsWith('http://127.0.0.1');
1124
+ const channelMcpUrl = isRemote ? existingMcpUrl : 'http://127.0.0.1:18963';
1125
+ const channelEnvFull = { MCP_URL: channelMcpUrl };
1126
+ // 保留已有的 MCP_AUTH_TOKEN(远程安装时写入),或从 server.json 读取
1127
+ const existingToken = existingChannel?.env?.MCP_AUTH_TOKEN;
1128
+ if (isRemote) {
1129
+ const token = existingToken || getAuthToken();
1130
+ if (token)
1131
+ channelEnvFull.MCP_AUTH_TOKEN = token;
1132
+ }
1119
1133
  claudeConfig.mcpServers['wecom-aibot-channel'] = {
1120
- command: 'node',
1121
- args: [binPath, '--channel'],
1122
- env: { MCP_URL: 'http://127.0.0.1:18963' },
1134
+ command: channelCmd.command,
1135
+ args: channelCmd.args,
1136
+ env: channelEnvFull,
1123
1137
  };
1124
1138
  console.log(`[config] full 模式:Channel MCP 使用本地路径`);
1125
1139
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vrs-soft/wecom-aibot-mcp",
3
- "version": "2.4.3",
3
+ "version": "2.4.4",
4
4
  "description": "企业微信智能机器人 MCP 服务 - Claude Code 审批通道",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",