@vrs-soft/wecom-aibot-mcp 2.4.2 → 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 +85 -27
- package/dist/config-wizard.d.ts +5 -0
- package/dist/config-wizard.js +73 -27
- package/dist/http-server.d.ts +4 -1
- package/dist/http-server.js +19 -6
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -13,7 +13,7 @@ import { spawn } from 'child_process';
|
|
|
13
13
|
import * as fs from 'fs';
|
|
14
14
|
import * as path from 'path';
|
|
15
15
|
import * as os from 'os';
|
|
16
|
-
import { runConfigWizard, loadConfig, saveConfig, deleteRobotConfigInteractive, uninstall, addMcpConfig, detectUserIdFromMessage, ensureHookInstalled, listAllRobots, ensureGlobalConfigs, getAuthToken, setAuthToken, updateMcpAuthHeaders, runRemoteInstallWizard, VERSION, } from './config-wizard.js';
|
|
16
|
+
import { runConfigWizard, loadConfig, saveConfig, deleteRobotConfigInteractive, uninstall, addMcpConfig, detectUserIdFromMessage, ensureHookInstalled, listAllRobots, ensureGlobalConfigs, getAuthToken, setAuthToken, getHttpsConfig, setHttpsConfig, updateMcpAuthHeaders, runRemoteInstallWizard, VERSION, } from './config-wizard.js';
|
|
17
17
|
import { initClient } from './client.js';
|
|
18
18
|
import { registerTools } from './tools/index.js';
|
|
19
19
|
import { startHttpServer, stopHttpServer, HTTP_PORT } from './http-server.js';
|
|
@@ -251,8 +251,10 @@ async function startMcpServerForeground(isDebug = false) {
|
|
|
251
251
|
logger.log(' ║ Claude Code 审批通道 ║');
|
|
252
252
|
logger.log(' ╚════════════════════════════════════════════════════════╝');
|
|
253
253
|
logger.log('');
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
const httpsConfig = getHttpsConfig() ?? undefined;
|
|
255
|
+
const protocol = httpsConfig ? 'HTTPS' : 'HTTP';
|
|
256
|
+
logger.log(`[mcp] 启动 MCP ${protocol} Server (端口: ${HTTP_PORT})...`);
|
|
257
|
+
await startHttpServer(server, HTTP_PORT, httpsConfig);
|
|
256
258
|
startKeepaliveMonitor();
|
|
257
259
|
logger.log(`[mcp] MCP Server 已就绪`);
|
|
258
260
|
logger.log(`[mcp] HTTP endpoint: http://127.0.0.1:${HTTP_PORT}/mcp`);
|
|
@@ -311,9 +313,15 @@ async function main() {
|
|
|
311
313
|
// 确定安装模式
|
|
312
314
|
const installMode = args.includes('--http-only') ? 'http-only' :
|
|
313
315
|
args.includes('--channel-only') ? 'channel-only' : 'full';
|
|
314
|
-
//
|
|
315
|
-
//
|
|
316
|
-
|
|
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) {
|
|
317
325
|
// 强制覆盖所有全局配置(不依赖智能体)
|
|
318
326
|
ensureGlobalConfigs(installMode);
|
|
319
327
|
}
|
|
@@ -504,39 +512,89 @@ async function main() {
|
|
|
504
512
|
console.log('[setup] 安装完成!请重启 Claude Code 以加载配置');
|
|
505
513
|
}
|
|
506
514
|
else if (wantServer) {
|
|
507
|
-
//
|
|
508
|
-
console.log('\n[setup] Server
|
|
509
|
-
|
|
510
|
-
if (!savedConfig?.botId)
|
|
511
|
-
await runConfigWizard();
|
|
515
|
+
// 服务器端:分两步——先完成 Server 安装,再配置机器人
|
|
516
|
+
console.log('\n[setup] ─── 步骤 1/2:Server 安装 ───\n');
|
|
517
|
+
console.log(' Server 负责运行 HTTP MCP 服务,Bot 配置在下一步单独完成\n');
|
|
512
518
|
const readline = await import('readline');
|
|
513
519
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
514
|
-
const token = await new Promise(resolve => rl.question('Auth Token(Client
|
|
520
|
+
const token = await new Promise(resolve => rl.question('Auth Token(Client 端连接时需填写相同 Token,留空跳过): ', a => { rl.close(); resolve(a.trim()); }));
|
|
515
521
|
if (token)
|
|
516
522
|
setAuthToken(token);
|
|
523
|
+
// HTTPS 证书配置
|
|
524
|
+
const defaultCertPath = path.join(os.homedir(), '.wecom-aibot-mcp', 'cert.pem');
|
|
525
|
+
console.log('\n HTTPS 证书配置(留空跳过,保持 HTTP 模式)');
|
|
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
|
+
};
|
|
541
|
+
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
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
|
+
}
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
console.log(`[setup] 跳过 HTTPS,使用 HTTP 模式`);
|
|
563
|
+
console.log(`[setup] 如需启用 HTTPS,配置证书后重新运行 --setup --server`);
|
|
564
|
+
}
|
|
517
565
|
console.log('\n[setup] Server 配置完成!');
|
|
518
566
|
console.log(' 启动: npx @vrs-soft/wecom-aibot-mcp --http-only --start');
|
|
567
|
+
console.log('\n[setup] ─── 步骤 2/2:配置企业微信机器人 ───\n');
|
|
568
|
+
await addMcpConfig();
|
|
519
569
|
}
|
|
520
570
|
else if (wantChannel) {
|
|
521
571
|
// Channel 客户端
|
|
522
572
|
console.log('\n[setup] Channel Client 安装模式\n');
|
|
523
|
-
|
|
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;
|
|
524
582
|
if (!mcpUrl) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
mcpUrl = await new Promise(resolve => rl.question('远程服务器地址(如 https://your-server:18963): ', a => { rl.close(); resolve(a.trim()); }));
|
|
528
|
-
if (!mcpUrl) {
|
|
529
|
-
console.log('[setup] ❌ 地址不能为空');
|
|
530
|
-
process.exit(1);
|
|
531
|
-
}
|
|
532
|
-
process.env.MCP_URL = mcpUrl;
|
|
583
|
+
console.log('[setup] ❌ 地址不能为空');
|
|
584
|
+
process.exit(1);
|
|
533
585
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
const
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
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);
|
|
540
598
|
}
|
|
541
599
|
ensureGlobalConfigs('channel-only');
|
|
542
600
|
console.log('[setup] Channel MCP 配置完成!请重启 Claude Code 以加载配置');
|
package/dist/config-wizard.d.ts
CHANGED
|
@@ -10,6 +10,11 @@ export declare const VERSION: string;
|
|
|
10
10
|
export declare function loadConfig(): WecomConfig | null;
|
|
11
11
|
export declare function getAuthToken(): string | undefined;
|
|
12
12
|
export declare function setAuthToken(token: string | undefined): boolean;
|
|
13
|
+
export declare function getHttpsConfig(): {
|
|
14
|
+
certPath: string;
|
|
15
|
+
keyPath: string;
|
|
16
|
+
} | null;
|
|
17
|
+
export declare function setHttpsConfig(certPath: string, keyPath: string): boolean;
|
|
13
18
|
export declare function updateMcpAuthHeaders(token?: string): void;
|
|
14
19
|
export declare function listAllMcpInstances(): Array<{
|
|
15
20
|
name: string;
|
package/dist/config-wizard.js
CHANGED
|
@@ -104,6 +104,38 @@ export function setAuthToken(token) {
|
|
|
104
104
|
fs.writeFileSync(SERVER_CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
105
105
|
return true;
|
|
106
106
|
}
|
|
107
|
+
// 获取 HTTPS 证书配置(从 server.json 读取)
|
|
108
|
+
export function getHttpsConfig() {
|
|
109
|
+
if (!fs.existsSync(SERVER_CONFIG_FILE))
|
|
110
|
+
return null;
|
|
111
|
+
try {
|
|
112
|
+
const config = JSON.parse(fs.readFileSync(SERVER_CONFIG_FILE, 'utf-8'));
|
|
113
|
+
if (config.certPath && config.keyPath) {
|
|
114
|
+
return { certPath: config.certPath, keyPath: config.keyPath };
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// 设置 HTTPS 证书配置(写入 server.json)
|
|
123
|
+
export function setHttpsConfig(certPath, keyPath) {
|
|
124
|
+
ensureConfigDir();
|
|
125
|
+
let config = {};
|
|
126
|
+
if (fs.existsSync(SERVER_CONFIG_FILE)) {
|
|
127
|
+
try {
|
|
128
|
+
config = JSON.parse(fs.readFileSync(SERVER_CONFIG_FILE, 'utf-8'));
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// ignore
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
config.certPath = certPath;
|
|
135
|
+
config.keyPath = keyPath;
|
|
136
|
+
fs.writeFileSync(SERVER_CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
107
139
|
// 更新 ~/.claude.json 中 wecom-aibot MCP 配置的 auth headers
|
|
108
140
|
export function updateMcpAuthHeaders(token) {
|
|
109
141
|
if (!fs.existsSync(CLAUDE_CONFIG_FILE))
|
|
@@ -1000,9 +1032,10 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
|
|
|
1000
1032
|
}
|
|
1001
1033
|
if (!claudeConfig.mcpServers)
|
|
1002
1034
|
claudeConfig.mcpServers = {};
|
|
1035
|
+
const mcpEndpointUrl = remoteOptions.url.replace(/\/+$/, '') + '/mcp';
|
|
1003
1036
|
claudeConfig.mcpServers['wecom-aibot'] = {
|
|
1004
1037
|
type: 'http',
|
|
1005
|
-
url:
|
|
1038
|
+
url: mcpEndpointUrl,
|
|
1006
1039
|
headers: { Authorization: `Bearer ${remoteOptions.token}` },
|
|
1007
1040
|
};
|
|
1008
1041
|
fs.writeFileSync(CLAUDE_CONFIG_FILE, JSON.stringify(claudeConfig, null, 2));
|
|
@@ -1012,8 +1045,8 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
|
|
|
1012
1045
|
}
|
|
1013
1046
|
// remote-channel 模式:写入远程 HTTP MCP(带 token)+ Channel MCP
|
|
1014
1047
|
if (mode === 'remote-channel') {
|
|
1015
|
-
if (!remoteOptions?.url
|
|
1016
|
-
console.log('[config] ❌ 远程模式需要提供 URL
|
|
1048
|
+
if (!remoteOptions?.url) {
|
|
1049
|
+
console.log('[config] ❌ 远程模式需要提供 URL');
|
|
1017
1050
|
return { upgraded: false, previousVersion };
|
|
1018
1051
|
}
|
|
1019
1052
|
let claudeConfig = {};
|
|
@@ -1022,21 +1055,20 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
|
|
|
1022
1055
|
}
|
|
1023
1056
|
if (!claudeConfig.mcpServers)
|
|
1024
1057
|
claudeConfig.mcpServers = {};
|
|
1025
|
-
// HTTP MCP 配置(带 token
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
headers
|
|
1030
|
-
|
|
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;
|
|
1031
1064
|
// Channel MCP 配置(带 MCP_URL + MCP_AUTH_TOKEN)
|
|
1032
|
-
const
|
|
1065
|
+
const channelEnvRemote = { MCP_URL: remoteOptions.url.replace(/\/+$/, '') };
|
|
1066
|
+
if (remoteOptions.token)
|
|
1067
|
+
channelEnvRemote.MCP_AUTH_TOKEN = remoteOptions.token;
|
|
1033
1068
|
claudeConfig.mcpServers['wecom-aibot-channel'] = {
|
|
1034
|
-
command: '
|
|
1035
|
-
args: [
|
|
1036
|
-
env:
|
|
1037
|
-
MCP_URL: remoteOptions.url,
|
|
1038
|
-
MCP_AUTH_TOKEN: remoteOptions.token,
|
|
1039
|
-
},
|
|
1069
|
+
command: 'npx',
|
|
1070
|
+
args: ['-y', '@vrs-soft/wecom-aibot-mcp', '--channel'],
|
|
1071
|
+
env: channelEnvRemote,
|
|
1040
1072
|
};
|
|
1041
1073
|
fs.writeFileSync(CLAUDE_CONFIG_FILE, JSON.stringify(claudeConfig, null, 2));
|
|
1042
1074
|
console.log('[config] remote-channel 模式:已写入 HTTP MCP + Channel MCP 配置(带 Token)');
|
|
@@ -1054,6 +1086,11 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
|
|
|
1054
1086
|
}
|
|
1055
1087
|
if (!claudeConfig.mcpServers)
|
|
1056
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'] };
|
|
1057
1094
|
if (mode === 'channel-only') {
|
|
1058
1095
|
// Channel-only 模式:必须通过 MCP_URL 指定远程地址
|
|
1059
1096
|
const mcpUrl = process.env.MCP_URL;
|
|
@@ -1062,19 +1099,17 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
|
|
|
1062
1099
|
console.log('[config] 请设置环境变量: MCP_URL=http://远程IP:18963');
|
|
1063
1100
|
return { upgraded: false, previousVersion };
|
|
1064
1101
|
}
|
|
1065
|
-
|
|
1066
|
-
const binPath = path.join(__dirname, 'bin.js');
|
|
1067
|
-
const channelEnv = { MCP_URL: mcpUrl };
|
|
1102
|
+
const channelEnv = { MCP_URL: mcpUrl.replace(/\/+$/, '') };
|
|
1068
1103
|
const authToken = getAuthToken();
|
|
1069
1104
|
if (authToken) {
|
|
1070
1105
|
channelEnv.MCP_AUTH_TOKEN = authToken;
|
|
1071
1106
|
}
|
|
1072
1107
|
claudeConfig.mcpServers['wecom-aibot-channel'] = {
|
|
1073
|
-
command:
|
|
1074
|
-
args:
|
|
1108
|
+
command: channelCmd.command,
|
|
1109
|
+
args: channelCmd.args,
|
|
1075
1110
|
env: channelEnv,
|
|
1076
1111
|
};
|
|
1077
|
-
console.log(`[config] Channel-only 模式:Channel MCP
|
|
1112
|
+
console.log(`[config] Channel-only 模式:Channel MCP 已配置`);
|
|
1078
1113
|
}
|
|
1079
1114
|
else {
|
|
1080
1115
|
// full 模式:同时写入 HTTP MCP 和 Channel MCP 配置
|
|
@@ -1082,12 +1117,23 @@ export function ensureGlobalConfigs(mode = 'full', remoteOptions) {
|
|
|
1082
1117
|
type: 'http',
|
|
1083
1118
|
url: 'http://127.0.0.1:18963/mcp',
|
|
1084
1119
|
};
|
|
1085
|
-
// Channel MCP
|
|
1086
|
-
const
|
|
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
|
+
}
|
|
1087
1133
|
claudeConfig.mcpServers['wecom-aibot-channel'] = {
|
|
1088
|
-
command:
|
|
1089
|
-
args:
|
|
1090
|
-
env:
|
|
1134
|
+
command: channelCmd.command,
|
|
1135
|
+
args: channelCmd.args,
|
|
1136
|
+
env: channelEnvFull,
|
|
1091
1137
|
};
|
|
1092
1138
|
console.log(`[config] full 模式:Channel MCP 使用本地路径`);
|
|
1093
1139
|
}
|
package/dist/http-server.d.ts
CHANGED
|
@@ -62,7 +62,10 @@ export declare function getCCRegistryEntry(ccId: string): CCRegistryEntry | null
|
|
|
62
62
|
export declare function getCCCount(): number;
|
|
63
63
|
export declare function getCCCountByRobot(robotName: string): number;
|
|
64
64
|
export declare function getOnlineCcIds(): string[];
|
|
65
|
-
export declare function startHttpServer(_server: McpServer, port?: number
|
|
65
|
+
export declare function startHttpServer(_server: McpServer, port?: number, httpsConfig?: {
|
|
66
|
+
certPath: string;
|
|
67
|
+
keyPath: string;
|
|
68
|
+
}): Promise<void>;
|
|
66
69
|
export declare function stopHttpServer(): void;
|
|
67
70
|
export declare function cleanupPortFile(): void;
|
|
68
71
|
export {};
|
package/dist/http-server.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - Session → robotName → WebSocket Connection
|
|
14
14
|
*/
|
|
15
15
|
import * as http from 'http';
|
|
16
|
+
import * as https from 'https';
|
|
16
17
|
import * as path from 'path';
|
|
17
18
|
import * as os from 'os';
|
|
18
19
|
import * as fs from 'fs';
|
|
@@ -461,12 +462,12 @@ ${onlineList.map(id => `• 【${id}】`).join('\n')}
|
|
|
461
462
|
示例:引用【${onlineList[0]}】的消息后回复`;
|
|
462
463
|
await client.sendText(reply);
|
|
463
464
|
}
|
|
464
|
-
export async function startHttpServer(_server, port = HTTP_PORT) {
|
|
465
|
+
export async function startHttpServer(_server, port = HTTP_PORT, httpsConfig) {
|
|
465
466
|
startTime = Date.now();
|
|
466
467
|
// 初始化 MCP Server
|
|
467
468
|
initMcpServer();
|
|
468
469
|
return new Promise((resolve, reject) => {
|
|
469
|
-
|
|
470
|
+
const requestHandler = async (req, res) => {
|
|
470
471
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
471
472
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
|
|
472
473
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Mcp-Session-Id');
|
|
@@ -758,7 +759,16 @@ export async function startHttpServer(_server, port = HTTP_PORT) {
|
|
|
758
759
|
}
|
|
759
760
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
760
761
|
res.end(JSON.stringify({ error: 'Not Found' }));
|
|
761
|
-
}
|
|
762
|
+
};
|
|
763
|
+
// 根据是否有 HTTPS 配置创建对应的 server
|
|
764
|
+
if (httpsConfig) {
|
|
765
|
+
const cert = fs.readFileSync(httpsConfig.certPath, 'utf-8');
|
|
766
|
+
const key = fs.readFileSync(httpsConfig.keyPath, 'utf-8');
|
|
767
|
+
httpServer = https.createServer({ cert, key }, requestHandler);
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
httpServer = http.createServer(requestHandler);
|
|
771
|
+
}
|
|
762
772
|
httpServer.on('error', (err) => {
|
|
763
773
|
if (err.code === 'EADDRINUSE') {
|
|
764
774
|
reject(new Error(`端口 ${port} 已被占用`));
|
|
@@ -767,9 +777,12 @@ export async function startHttpServer(_server, port = HTTP_PORT) {
|
|
|
767
777
|
reject(err);
|
|
768
778
|
}
|
|
769
779
|
});
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
780
|
+
// HTTPS 模式绑定所有网卡(供远程客户端访问),HTTP 模式只绑本地
|
|
781
|
+
const host = httpsConfig ? '0.0.0.0' : '127.0.0.1';
|
|
782
|
+
const protocol = httpsConfig ? 'https' : 'http';
|
|
783
|
+
httpServer.listen(port, host, async () => {
|
|
784
|
+
logger.log(`[http] MCP Server 已启动: ${protocol}://${host}:${port}`);
|
|
785
|
+
logger.log(`[http] MCP endpoint: ${protocol}://${host}:${port}/mcp (stateless mode)`);
|
|
773
786
|
// 自动连接所有配置的机器人
|
|
774
787
|
await connectAllRobots();
|
|
775
788
|
resolve();
|