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/package.json CHANGED
@@ -1,24 +1,24 @@
1
- {
2
- "name": "cc-wechat",
3
- "version": "0.4.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
- // ─── 二进制模式补丁定义(exe 专用)────────────────────────
19
- const BINARY_PATCHES = [
20
- ['function PaH(){return lA("tengu_harbor",!1)}', 'function PaH(){return !0 }', 'Channels feature flag (tengu_harbor)'],
21
- ['if(!yf()?.accessToken)', 'if( false )', 'Channel gate auth check'],
22
- ['noAuth:!yf()?.accessToken', 'noAuth: false ', 'UI noAuth display check'],
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 [original, replacement, desc] of BINARY_PATCHES) {
136
- if (original.length !== replacement.length) {
137
- console.error(` 致命错误: "${desc}" 补丁长度不匹配`);
138
- process.exit(1);
139
- }
140
- const origBuf = Buffer.from(original);
141
- const patchBuf = Buffer.from(replacement);
142
-
143
- let pos = 0, count = 0;
144
- while (true) {
145
- const idx = buf.indexOf(origBuf, pos);
146
- if (idx === -1) break;
147
- patchBuf.copy(buf, idx);
148
- count++;
149
- pos = idx + 1;
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
- let alreadyCount = 0;
153
- pos = 0;
154
- while (true) {
155
- const idx = buf.indexOf(patchBuf, pos);
156
- if (idx === -1) break;
157
- alreadyCount++;
158
- pos = idx + 1;
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 (count > 0) { console.log(` ${desc} — ${count} 处已修补`); patched += count; }
162
- else if (alreadyCount > 0) { console.log(` ⏭️ ${desc} — 已修补 (${alreadyCount} 处)`); skipped += alreadyCount; }
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; }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-channel-patch",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "One-command patch to enable Claude Code Channels (bypasses tengu_harbor feature flag)",
5
5
  "type": "module",
6
6
  "bin": {
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
- import { execSync } from 'node:child_process';
7
- import path from 'node:path';
8
- import { fileURLToPath } from 'node:url';
9
- import { loginTerminal } from './auth.js';
10
- import { saveAccount, getActiveAccount } from './store.js';
11
-
12
- // ─── help ────────────────────────────────────────────
13
-
14
- /** 打印帮助信息 */
15
- function help(): void {
16
- console.log(`
17
- cc-wechat 微信 Claude Code Channel 插件
18
-
19
- 用法: npx cc-wechat <命令>
20
-
21
- 命令:
22
- install 注册 MCP server + 扫码登录
23
- patch 修补 Claude Code 以启用 Channels 功能
24
- unpatch 恢复原始 Claude Code
25
- login 重新扫码登录
26
- status 查看连接状态
27
- help 显示帮助
28
- `);
29
- }
30
-
31
- // ─── install ─────────────────────────────────────────
32
-
33
- /** 注册 MCP server + 扫码登录 */
34
- async function install(): Promise<void> {
35
- console.log('\n🔧 cc-wechat 安装向导\n');
36
-
37
- // [1/3] 注册 MCP server
38
- console.log('[1/3] 注册 MCP server...');
39
- const serverPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'server.js');
40
-
41
- try {
42
- execSync('claude mcp add -s user wechat-channel node ' + serverPath, { stdio: 'pipe' });
43
- console.log(' ✅ MCP server 已注册');
44
- } catch {
45
- try {
46
- execSync('claude mcp remove wechat-channel', { stdio: 'pipe' });
47
- execSync('claude mcp add -s user wechat-channel node ' + serverPath, { stdio: 'pipe' });
48
- console.log(' ✅ MCP server 已重新注册');
49
- } catch (e) {
50
- console.error(' MCP server 注册失败:', (e as Error).message);
51
- }
52
- }
53
-
54
- // [2/3] 微信扫码登录
55
- console.log('\n[2/3] 微信扫码登录...');
56
- const existing = getActiveAccount();
57
- if (existing) {
58
- console.log(' ⏭️ 已有登录账号,跳过扫码。如需重新登录请运行: npx cc-wechat login');
59
- } else {
60
- const { token, accountId, baseUrl } = await loginTerminal();
61
- saveAccount({
62
- token,
63
- baseUrl: baseUrl ?? '',
64
- botId: accountId.replace(/@/g, '-').replace(/\./g, '-'),
65
- savedAt: new Date().toISOString(),
66
- });
67
- console.log(' 登录成功');
68
- }
69
-
70
- // [3/3] 完成
71
- console.log('\n[3/3] 安装完成!');
72
- console.log('\n启动 Claude Code 时使用:');
73
- console.log(' claude --dangerously-load-development-channels server:wechat-channel\n');
74
- }
75
-
76
- // ─── login ───────────────────────────────────────────
77
-
78
- /** 重新扫码登录 */
79
- async function login(): Promise<void> {
80
- console.log('\n🔑 微信扫码登录\n');
81
- const { token, accountId, baseUrl } = await loginTerminal();
82
- saveAccount({
83
- token,
84
- baseUrl: baseUrl ?? '',
85
- botId: accountId.replace(/@/g, '-').replace(/\./g, '-'),
86
- savedAt: new Date().toISOString(),
87
- });
88
- console.log('\n✅ 登录成功!账号已保存。\n');
89
- }
90
-
91
- // ─── status ──────────────────────────────────────────
92
-
93
- /** 查看连接状态 */
94
- function status(): void {
95
- const account = getActiveAccount();
96
- if (account) {
97
- console.log('\n📋 当前账号状态:\n');
98
- console.log(` botId: ${account.botId.substring(0, 12)}...`);
99
- console.log(` baseUrl: ${account.baseUrl}`);
100
- console.log(` savedAt: ${account.savedAt}\n`);
101
- } else {
102
- console.log('\n❌ 尚未登录。请运行: npx cc-wechat install\n');
103
- }
104
- }
105
-
106
- // ─── main ────────────────────────────────────────────
107
-
108
- const command = process.argv[2];
109
- switch (command) {
110
- case 'install': case 'setup': install(); break;
111
- case 'login': login(); break;
112
- case 'patch': case 'unpatch': {
113
- // 动态导入 patch 模块,传递命令
114
- process.argv[2] = command;
115
- await import('./patch.js');
116
- break;
117
- }
118
- case 'status': status(); break;
119
- case 'help': case '--help': case '-h': case undefined: help(); break;
120
- default: console.error(`未知命令: ${command}`); help(); process.exit(1);
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
- const BINARY_PATCHES: Array<[string, string, string]> = [
16
- ['function PaH(){return lA("tengu_harbor",!1)}', 'function PaH(){return !0 }', 'Channels feature flag (tengu_harbor)'],
17
- ['if(!yf()?.accessToken)', 'if( false )', 'Channel gate auth check'],
18
- ['noAuth:!yf()?.accessToken', 'noAuth: false ', 'UI noAuth display check'],
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 [original, replacement, desc] of BINARY_PATCHES) {
147
- const origBuf = Buffer.from(original);
148
- const patchBuf = Buffer.from(replacement);
149
- let pos = 0, count = 0;
150
- while (true) { const idx = buf.indexOf(origBuf, pos); if (idx === -1) break; patchBuf.copy(buf, idx); count++; pos = idx + 1; }
151
- let alreadyCount = 0; pos = 0;
152
- while (true) { const idx = buf.indexOf(patchBuf, pos); if (idx === -1) break; alreadyCount++; pos = idx + 1; }
153
-
154
- if (count > 0) { console.log(` ✅ ${desc} ${count} 处已修补`); patched += count; }
155
- else if (alreadyCount > 0) { console.log(` ⏭️ ${desc} — 已修补`); skipped += alreadyCount; }
156
- else { console.log(` ⚠️ ${desc} 未找到`); }
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
+ }