cc-wechat 0.4.0 → 0.5.1
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/README.md +116 -111
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +14 -12
- package/dist/cli.js.map +1 -1
- package/dist/patch.js +57 -29
- package/dist/patch.js.map +1 -1
- package/dist/proxy.d.ts +5 -0
- package/dist/proxy.js +20 -0
- package/dist/proxy.js.map +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +6 -4
- package/dist/server.js.map +1 -1
- package/package.json +24 -24
- package/packages/cc-channel-patch/index.mjs +66 -31
- package/packages/cc-channel-patch/package.json +1 -1
- package/src/cli.ts +124 -121
- package/src/patch.ts +47 -15
- package/src/proxy.ts +19 -0
- package/src/server.ts +463 -460
package/package.json
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "cc-wechat",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "WeChat channel for Claude Code via iLink Bot API",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"cc-wechat": "dist/cli.js",
|
|
8
|
-
"cc-wechat-server": "dist/server.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"dev": "tsc --watch"
|
|
13
|
-
},
|
|
14
|
-
"engines": { "node": ">=22" },
|
|
15
|
-
"license": "MIT",
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
18
|
-
"qrcode-terminal": "^0.12.0"
|
|
19
|
-
},
|
|
20
|
-
"devDependencies": {
|
|
21
|
-
"@types/node": "^22.0.0",
|
|
22
|
-
"typescript": "^5.7.0"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "cc-wechat",
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "WeChat channel for Claude Code via iLink Bot API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cc-wechat": "dist/cli.js",
|
|
8
|
+
"cc-wechat-server": "dist/server.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch"
|
|
13
|
+
},
|
|
14
|
+
"engines": { "node": ">=22" },
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
18
|
+
"qrcode-terminal": "^0.12.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^22.0.0",
|
|
22
|
+
"typescript": "^5.7.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -15,11 +15,30 @@ import path from 'node:path';
|
|
|
15
15
|
import { homedir } from 'node:os';
|
|
16
16
|
import { execSync } from 'node:child_process';
|
|
17
17
|
|
|
18
|
-
// ───
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
// ─── 二进制模式补丁定义 ────────────────────────────────────
|
|
19
|
+
// 每组:多个 [原始, 替换] 变体,匹配到任一即生效(适配不同平台 minify 结果)
|
|
20
|
+
const BINARY_PATCH_GROUPS = [
|
|
21
|
+
{
|
|
22
|
+
desc: 'Channels feature flag (tengu_harbor)',
|
|
23
|
+
variants: [
|
|
24
|
+
['function PaH(){return lA("tengu_harbor",!1)}', 'function PaH(){return !0 }'],
|
|
25
|
+
['function waH(){return l$("tengu_harbor",!1)}', 'function waH(){return !0 }'],
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
desc: 'Channel gate auth check',
|
|
30
|
+
variants: [
|
|
31
|
+
['if(!yf()?.accessToken)', 'if( false )'],
|
|
32
|
+
['if(!SL()?.accessToken)', 'if( false )'],
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
desc: 'UI noAuth display check',
|
|
37
|
+
variants: [
|
|
38
|
+
['noAuth:!yf()?.accessToken', 'noAuth: false '],
|
|
39
|
+
['noAuth:!SL()?.accessToken', 'noAuth: false '],
|
|
40
|
+
],
|
|
41
|
+
},
|
|
23
42
|
];
|
|
24
43
|
|
|
25
44
|
// ─── 查找 claude 可执行文件 ──────────────────────────────
|
|
@@ -132,35 +151,51 @@ function patchBinary(exePath) {
|
|
|
132
151
|
const buf = fs.readFileSync(exePath);
|
|
133
152
|
let patched = 0, skipped = 0;
|
|
134
153
|
|
|
135
|
-
for (const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
for (const { desc, variants } of BINARY_PATCH_GROUPS) {
|
|
155
|
+
let groupPatched = false, groupSkipped = false;
|
|
156
|
+
|
|
157
|
+
for (const [original, replacement] of variants) {
|
|
158
|
+
if (original.length !== replacement.length) {
|
|
159
|
+
console.error(` 致命错误: "${desc}" 补丁长度不匹配 (${original.length} vs ${replacement.length})`);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
const origBuf = Buffer.from(original);
|
|
163
|
+
const patchBuf = Buffer.from(replacement);
|
|
164
|
+
|
|
165
|
+
// 搜索并替换
|
|
166
|
+
let pos = 0, count = 0;
|
|
167
|
+
while (true) {
|
|
168
|
+
const idx = buf.indexOf(origBuf, pos);
|
|
169
|
+
if (idx === -1) break;
|
|
170
|
+
patchBuf.copy(buf, idx);
|
|
171
|
+
count++;
|
|
172
|
+
pos = idx + 1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (count > 0) {
|
|
176
|
+
console.log(` ✅ ${desc} — ${count} 处已修补`);
|
|
177
|
+
patched += count;
|
|
178
|
+
groupPatched = true;
|
|
179
|
+
break; // 一组只需匹配一个变体
|
|
180
|
+
}
|
|
151
181
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
182
|
+
// 检查是否已 patch
|
|
183
|
+
let alreadyCount = 0;
|
|
184
|
+
pos = 0;
|
|
185
|
+
while (true) {
|
|
186
|
+
const idx = buf.indexOf(patchBuf, pos);
|
|
187
|
+
if (idx === -1) break;
|
|
188
|
+
alreadyCount++;
|
|
189
|
+
pos = idx + 1;
|
|
190
|
+
}
|
|
191
|
+
if (alreadyCount > 0) {
|
|
192
|
+
groupSkipped = true;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
159
195
|
}
|
|
160
196
|
|
|
161
|
-
if (
|
|
162
|
-
else if (
|
|
163
|
-
else { console.log(` ⚠️ ${desc} — 未找到`); }
|
|
197
|
+
if (!groupPatched && groupSkipped) { console.log(` ⏭️ ${desc} — 已修补`); skipped++; }
|
|
198
|
+
else if (!groupPatched && !groupSkipped) { console.log(` ⚠️ ${desc} — 未找到`); }
|
|
164
199
|
}
|
|
165
200
|
|
|
166
201
|
if (patched === 0 && skipped > 0) { console.log('\n 所有补丁已生效,无需操作。\n'); return; }
|
package/src/cli.ts
CHANGED
|
@@ -1,121 +1,124 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* cc-wechat CLI 入口 — install/login/status/help 命令
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
console.log('
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
console.log(
|
|
101
|
-
}
|
|
102
|
-
console.log(
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cc-wechat CLI 入口 — install/login/status/help 命令
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 代理支持(必须最先导入)
|
|
7
|
+
import './proxy.js';
|
|
8
|
+
|
|
9
|
+
import { execSync } from 'node:child_process';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { loginTerminal } from './auth.js';
|
|
13
|
+
import { saveAccount, getActiveAccount } from './store.js';
|
|
14
|
+
|
|
15
|
+
// ─── help ────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
/** 打印帮助信息 */
|
|
18
|
+
function help(): void {
|
|
19
|
+
console.log(`
|
|
20
|
+
cc-wechat — 微信 Claude Code Channel 插件
|
|
21
|
+
|
|
22
|
+
用法: npx cc-wechat <命令>
|
|
23
|
+
|
|
24
|
+
命令:
|
|
25
|
+
install 注册 MCP server + 扫码登录
|
|
26
|
+
patch 修补 Claude Code 以启用 Channels 功能
|
|
27
|
+
unpatch 恢复原始 Claude Code
|
|
28
|
+
login 重新扫码登录
|
|
29
|
+
status 查看连接状态
|
|
30
|
+
help 显示帮助
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ─── install ─────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
/** 注册 MCP server + 扫码登录 */
|
|
37
|
+
async function install(): Promise<void> {
|
|
38
|
+
console.log('\n🔧 cc-wechat 安装向导\n');
|
|
39
|
+
|
|
40
|
+
// [1/3] 注册 MCP server
|
|
41
|
+
console.log('[1/3] 注册 MCP server...');
|
|
42
|
+
const serverPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'server.js');
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
execSync('claude mcp add -s user wechat-channel node ' + serverPath, { stdio: 'pipe' });
|
|
46
|
+
console.log(' ✅ MCP server 已注册');
|
|
47
|
+
} catch {
|
|
48
|
+
try {
|
|
49
|
+
execSync('claude mcp remove wechat-channel', { stdio: 'pipe' });
|
|
50
|
+
execSync('claude mcp add -s user wechat-channel node ' + serverPath, { stdio: 'pipe' });
|
|
51
|
+
console.log(' ✅ MCP server 已重新注册');
|
|
52
|
+
} catch (e) {
|
|
53
|
+
console.error(' ❌ MCP server 注册失败:', (e as Error).message);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// [2/3] 微信扫码登录
|
|
58
|
+
console.log('\n[2/3] 微信扫码登录...');
|
|
59
|
+
const existing = getActiveAccount();
|
|
60
|
+
if (existing) {
|
|
61
|
+
console.log(' ⏭️ 已有登录账号,跳过扫码。如需重新登录请运行: npx cc-wechat login');
|
|
62
|
+
} else {
|
|
63
|
+
const { token, accountId, baseUrl } = await loginTerminal();
|
|
64
|
+
saveAccount({
|
|
65
|
+
token,
|
|
66
|
+
baseUrl: baseUrl ?? '',
|
|
67
|
+
botId: accountId.replace(/@/g, '-').replace(/\./g, '-'),
|
|
68
|
+
savedAt: new Date().toISOString(),
|
|
69
|
+
});
|
|
70
|
+
console.log(' ✅ 登录成功');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// [3/3] 完成
|
|
74
|
+
console.log('\n[3/3] 安装完成!');
|
|
75
|
+
console.log('\n启动 Claude Code 时使用:');
|
|
76
|
+
console.log(' claude --dangerously-load-development-channels server:wechat-channel\n');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── login ───────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
/** 重新扫码登录 */
|
|
82
|
+
async function login(): Promise<void> {
|
|
83
|
+
console.log('\n🔑 微信扫码登录\n');
|
|
84
|
+
const { token, accountId, baseUrl } = await loginTerminal();
|
|
85
|
+
saveAccount({
|
|
86
|
+
token,
|
|
87
|
+
baseUrl: baseUrl ?? '',
|
|
88
|
+
botId: accountId.replace(/@/g, '-').replace(/\./g, '-'),
|
|
89
|
+
savedAt: new Date().toISOString(),
|
|
90
|
+
});
|
|
91
|
+
console.log('\n✅ 登录成功!账号已保存。\n');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── status ──────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
/** 查看连接状态 */
|
|
97
|
+
function status(): void {
|
|
98
|
+
const account = getActiveAccount();
|
|
99
|
+
if (account) {
|
|
100
|
+
console.log('\n📋 当前账号状态:\n');
|
|
101
|
+
console.log(` botId: ${account.botId.substring(0, 12)}...`);
|
|
102
|
+
console.log(` baseUrl: ${account.baseUrl}`);
|
|
103
|
+
console.log(` savedAt: ${account.savedAt}\n`);
|
|
104
|
+
} else {
|
|
105
|
+
console.log('\n❌ 尚未登录。请运行: npx cc-wechat install\n');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─── main ────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
const command = process.argv[2];
|
|
112
|
+
switch (command) {
|
|
113
|
+
case 'install': case 'setup': install(); break;
|
|
114
|
+
case 'login': login(); break;
|
|
115
|
+
case 'patch': case 'unpatch': {
|
|
116
|
+
// 动态导入 patch 模块,传递命令
|
|
117
|
+
process.argv[2] = command;
|
|
118
|
+
await import('./patch.js');
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case 'status': status(); break;
|
|
122
|
+
case 'help': case '--help': case '-h': case undefined: help(); break;
|
|
123
|
+
default: console.error(`未知命令: ${command}`); help(); process.exit(1);
|
|
124
|
+
}
|
package/src/patch.ts
CHANGED
|
@@ -12,10 +12,29 @@ import { execSync } from 'node:child_process';
|
|
|
12
12
|
import { createRequire } from 'node:module';
|
|
13
13
|
|
|
14
14
|
// ─── 二进制模式补丁定义 ──────────────────────────────────
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
// 每组多个变体,适配不同平台的 minify 结果(Windows/Linux/macOS 函数名不同)
|
|
16
|
+
const BINARY_PATCH_GROUPS: Array<{ desc: string; variants: Array<[string, string]> }> = [
|
|
17
|
+
{
|
|
18
|
+
desc: 'Channels feature flag (tengu_harbor)',
|
|
19
|
+
variants: [
|
|
20
|
+
['function PaH(){return lA("tengu_harbor",!1)}', 'function PaH(){return !0 }'],
|
|
21
|
+
['function waH(){return l$("tengu_harbor",!1)}', 'function waH(){return !0 }'],
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
desc: 'Channel gate auth check',
|
|
26
|
+
variants: [
|
|
27
|
+
['if(!yf()?.accessToken)', 'if( false )'],
|
|
28
|
+
['if(!SL()?.accessToken)', 'if( false )'],
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
desc: 'UI noAuth display check',
|
|
33
|
+
variants: [
|
|
34
|
+
['noAuth:!yf()?.accessToken', 'noAuth: false '],
|
|
35
|
+
['noAuth:!SL()?.accessToken', 'noAuth: false '],
|
|
36
|
+
],
|
|
37
|
+
},
|
|
19
38
|
];
|
|
20
39
|
|
|
21
40
|
// ─── 查找 claude ──────────────────────────────────────────
|
|
@@ -143,17 +162,30 @@ function patchBinary(exePath: string): void {
|
|
|
143
162
|
const buf = fs.readFileSync(exePath);
|
|
144
163
|
let patched = 0, skipped = 0;
|
|
145
164
|
|
|
146
|
-
for (const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
165
|
+
for (const { desc, variants } of BINARY_PATCH_GROUPS) {
|
|
166
|
+
let groupPatched = false, groupSkipped = false;
|
|
167
|
+
|
|
168
|
+
for (const [original, replacement] of variants) {
|
|
169
|
+
const origBuf = Buffer.from(original);
|
|
170
|
+
const patchBuf = Buffer.from(replacement);
|
|
171
|
+
|
|
172
|
+
let pos = 0, count = 0;
|
|
173
|
+
while (true) { const idx = buf.indexOf(origBuf, pos); if (idx === -1) break; patchBuf.copy(buf, idx); count++; pos = idx + 1; }
|
|
174
|
+
|
|
175
|
+
if (count > 0) {
|
|
176
|
+
console.log(` ✅ ${desc} — ${count} 处已修补`);
|
|
177
|
+
patched += count;
|
|
178
|
+
groupPatched = true;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let alreadyCount = 0; pos = 0;
|
|
183
|
+
while (true) { const idx = buf.indexOf(patchBuf, pos); if (idx === -1) break; alreadyCount++; pos = idx + 1; }
|
|
184
|
+
if (alreadyCount > 0) { groupSkipped = true; break; }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!groupPatched && groupSkipped) { console.log(` ⏭️ ${desc} — 已修补`); skipped++; }
|
|
188
|
+
else if (!groupPatched && !groupSkipped) { console.log(` ⚠️ ${desc} — 未找到`); }
|
|
157
189
|
}
|
|
158
190
|
|
|
159
191
|
if (patched === 0 && skipped > 0) { console.log('\n 所有补丁已生效。\n'); return; }
|
package/src/proxy.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 代理支持 — 检测 HTTPS_PROXY/HTTP_PROXY 环境变量,设置全局 fetch dispatcher
|
|
3
|
+
* 在入口文件最先导入,确保所有 fetch 调用都走代理
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy
|
|
7
|
+
|| process.env.HTTP_PROXY || process.env.http_proxy;
|
|
8
|
+
|
|
9
|
+
if (proxyUrl) {
|
|
10
|
+
try {
|
|
11
|
+
// Node.js 22+ 内置 undici ProxyAgent
|
|
12
|
+
// @ts-ignore — undici 内置于 Node.js 22+ 但无独立类型声明
|
|
13
|
+
const { ProxyAgent, setGlobalDispatcher } = await import('undici');
|
|
14
|
+
setGlobalDispatcher(new ProxyAgent(proxyUrl));
|
|
15
|
+
process.stderr.write(`[wechat-channel] 使用代理: ${proxyUrl}\n`);
|
|
16
|
+
} catch {
|
|
17
|
+
process.stderr.write(`[wechat-channel] 警告: 设置代理失败,将直连\n`);
|
|
18
|
+
}
|
|
19
|
+
}
|