@vrs-soft/wecom-aibot-mcp 2.4.25 → 3.0.0-rc.0
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 +55 -19
- package/dist/channel-server.js +1 -41
- package/dist/config-wizard.d.ts +9 -1
- package/dist/config-wizard.js +83 -388
- package/dist/hooks/permission-hook.d.ts +2 -0
- package/dist/hooks/permission-hook.js +325 -0
- package/dist/hooks/stop-hook.d.ts +2 -0
- package/dist/hooks/stop-hook.js +101 -0
- package/dist/http-server.js +2 -2
- package/dist/platform.d.ts +14 -0
- package/dist/platform.js +107 -0
- package/dist/project-config.js +9 -4
- package/dist/tools/index.js +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -13,7 +13,7 @@ import { spawn, execSync } 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, getHttpsConfig, setHttpsConfig, updateMcpAuthHeaders, runRemoteInstallWizard, VERSION, } from './config-wizard.js';
|
|
16
|
+
import { runConfigWizard, loadConfig, saveConfig, deleteRobotConfigInteractive, uninstall, addMcpConfig, detectUserIdFromMessage, ensureHookInstalled, listAllRobots, ensureGlobalConfigs, getInstalledMode, 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';
|
|
@@ -182,23 +182,36 @@ function isServerRunning() {
|
|
|
182
182
|
return false;
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
|
-
// 通过端口查找进程 PID(fallback,当 PID
|
|
185
|
+
// 通过端口查找进程 PID(fallback,当 PID 文件不存在时)。
|
|
186
|
+
// Windows 用 netstat -ano;Unix 优先 lsof,回退 ss。
|
|
186
187
|
function findPidByPort(port) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
188
|
+
if (process.platform === 'win32') {
|
|
189
|
+
try {
|
|
190
|
+
const output = execSync(`netstat -ano -p TCP`, { encoding: 'utf-8' });
|
|
191
|
+
// 行形如: " TCP 127.0.0.1:18963 0.0.0.0:0 LISTENING 1234"
|
|
192
|
+
const re = new RegExp(`^\\s*TCP\\s+\\S+:${port}\\s+\\S+\\s+LISTENING\\s+(\\d+)`, 'm');
|
|
193
|
+
const m = output.match(re);
|
|
194
|
+
if (m)
|
|
195
|
+
return parseInt(m[1], 10);
|
|
196
|
+
}
|
|
197
|
+
catch { /* ignore */ }
|
|
198
|
+
return null;
|
|
193
199
|
}
|
|
194
|
-
catch { /* ignore */ }
|
|
195
200
|
try {
|
|
196
|
-
// macOS
|
|
201
|
+
// macOS / Linux 都装了 lsof
|
|
197
202
|
const output = execSync(`lsof -ti :${port} 2>/dev/null`, { encoding: 'utf-8' }).trim();
|
|
198
203
|
if (output)
|
|
199
204
|
return parseInt(output.split('\n')[0]);
|
|
200
205
|
}
|
|
201
206
|
catch { /* ignore */ }
|
|
207
|
+
try {
|
|
208
|
+
// Linux 备选:ss -tlnp
|
|
209
|
+
const output = execSync(`ss -tlnp 2>/dev/null | grep ':${port}'`, { encoding: 'utf-8' });
|
|
210
|
+
const match = output.match(/pid=(\d+)/);
|
|
211
|
+
if (match)
|
|
212
|
+
return parseInt(match[1]);
|
|
213
|
+
}
|
|
214
|
+
catch { /* ignore */ }
|
|
202
215
|
return null;
|
|
203
216
|
}
|
|
204
217
|
// 停止服务
|
|
@@ -373,9 +386,14 @@ function startMcpServerBackground() {
|
|
|
373
386
|
}
|
|
374
387
|
async function main() {
|
|
375
388
|
const args = process.argv.slice(2);
|
|
376
|
-
//
|
|
377
|
-
const
|
|
378
|
-
args.includes('--channel-only') ? 'channel-only' :
|
|
389
|
+
// 确定安装模式:优先 CLI flag,其次复用 version.json 里上次的 mode(保持 remote / channel-only 等模式不被 --upgrade 打回 full)
|
|
390
|
+
const explicitMode = args.includes('--http-only') ? 'http-only' :
|
|
391
|
+
args.includes('--channel-only') ? 'channel-only' : undefined;
|
|
392
|
+
const prior = getInstalledMode();
|
|
393
|
+
const installMode = explicitMode || prior.mode || 'full';
|
|
394
|
+
const remoteOptions = (installMode === 'remote' || installMode === 'remote-channel') && prior.remote?.url
|
|
395
|
+
? { url: prior.remote.url, token: prior.remote.token || '' }
|
|
396
|
+
: undefined;
|
|
379
397
|
// 以下命令跳过顶部 ensureGlobalConfigs,避免覆盖配置
|
|
380
398
|
// --setup: 向导完成后自己调用
|
|
381
399
|
// --channel: 作为 Channel MCP 代理运行,不应改写全局配置
|
|
@@ -390,7 +408,17 @@ async function main() {
|
|
|
390
408
|
args.includes('--clean-cache') || args.includes('--set-token') || args.includes('--config');
|
|
391
409
|
if (!skipEnsure) {
|
|
392
410
|
// 强制覆盖所有全局配置(不依赖智能体)
|
|
393
|
-
|
|
411
|
+
if (installMode === 'remote' || installMode === 'remote-channel') {
|
|
412
|
+
if (remoteOptions) {
|
|
413
|
+
ensureGlobalConfigs(installMode, remoteOptions);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
console.log(`[mcp] 检测到上次安装模式 ${installMode},但缺少远程参数;跳过配置写入。如需变更请使用 --setup`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
ensureGlobalConfigs(installMode);
|
|
421
|
+
}
|
|
394
422
|
}
|
|
395
423
|
// 解析命令行参数
|
|
396
424
|
if (args.includes('--help') || args.includes('-h')) {
|
|
@@ -418,7 +446,13 @@ async function main() {
|
|
|
418
446
|
const CLAUDE_CONFIG_FILE = path.join(os.homedir(), '.claude.json');
|
|
419
447
|
const CLAUDE_SETTINGS_FILE = path.join(os.homedir(), '.claude', 'settings.local.json');
|
|
420
448
|
const VERSION_FILE = path.join(os.homedir(), '.wecom-aibot-mcp', 'version.json');
|
|
421
|
-
const
|
|
449
|
+
const HOOK_SCRIPTS = [
|
|
450
|
+
path.join(os.homedir(), '.wecom-aibot-mcp', 'permission-hook.js'),
|
|
451
|
+
path.join(os.homedir(), '.wecom-aibot-mcp', 'stop-hook.js'),
|
|
452
|
+
// 旧版 .sh 残留
|
|
453
|
+
path.join(os.homedir(), '.wecom-aibot-mcp', 'permission-hook.sh'),
|
|
454
|
+
path.join(os.homedir(), '.wecom-aibot-mcp', 'stop-hook.sh'),
|
|
455
|
+
];
|
|
422
456
|
// 1. 删除 ~/.claude.json 中的 wecom-aibot 配置
|
|
423
457
|
if (fs.existsSync(CLAUDE_CONFIG_FILE)) {
|
|
424
458
|
const content = fs.readFileSync(CLAUDE_CONFIG_FILE, 'utf-8');
|
|
@@ -451,10 +485,12 @@ async function main() {
|
|
|
451
485
|
fs.unlinkSync(VERSION_FILE);
|
|
452
486
|
console.log('[mcp] 已删除 ~/.wecom-aibot-mcp/version.json');
|
|
453
487
|
}
|
|
454
|
-
// 4. 删除 hook
|
|
455
|
-
|
|
456
|
-
fs.
|
|
457
|
-
|
|
488
|
+
// 4. 删除 hook 脚本(含旧版 .sh)
|
|
489
|
+
for (const p of HOOK_SCRIPTS) {
|
|
490
|
+
if (fs.existsSync(p)) {
|
|
491
|
+
fs.unlinkSync(p);
|
|
492
|
+
console.log(`[mcp] 已删除 ${p}`);
|
|
493
|
+
}
|
|
458
494
|
}
|
|
459
495
|
// 5. 重新安装全局配置
|
|
460
496
|
logger.log('\n[mcp] 正在重新安装...');
|
package/dist/channel-server.js
CHANGED
|
@@ -12,50 +12,10 @@
|
|
|
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';
|
|
16
15
|
import { VERSION, installSkill } from './config-wizard.js';
|
|
17
16
|
import { addPermissionHook, registerActiveProject, unregisterActiveProject, updateWechatModeConfig } from './project-config.js';
|
|
18
17
|
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
|
+
import { findClaudePid } from './platform.js';
|
|
59
19
|
const MCP_URL = process.env.MCP_URL || 'http://127.0.0.1:18963';
|
|
60
20
|
const MCP_AUTH_TOKEN = process.env.MCP_AUTH_TOKEN;
|
|
61
21
|
// 构建带 auth 的 fetch headers
|
package/dist/config-wizard.d.ts
CHANGED
|
@@ -40,7 +40,15 @@ export declare function getDocMcpUrl(robotName?: string): {
|
|
|
40
40
|
error?: string;
|
|
41
41
|
};
|
|
42
42
|
export declare function ensureHookInstalled(): void;
|
|
43
|
-
export
|
|
43
|
+
export type InstallMode = 'full' | 'http-only' | 'channel-only' | 'remote' | 'remote-channel';
|
|
44
|
+
export declare function getInstalledMode(): {
|
|
45
|
+
mode?: InstallMode;
|
|
46
|
+
remote?: {
|
|
47
|
+
url: string;
|
|
48
|
+
token?: string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export declare function ensureGlobalConfigs(mode?: InstallMode, remoteOptions?: {
|
|
44
52
|
url: string;
|
|
45
53
|
token: string;
|
|
46
54
|
}): {
|