@unlinearity/cli-wechat-bridge 1.0.4 → 1.0.6
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 +136 -25
- package/bin/wechat-daemon.mjs +5 -0
- package/dist/bridge/bridge-adapters.claude.js +33 -3
- package/dist/bridge/bridge-adapters.codex.js +216 -6
- package/dist/bridge/bridge-adapters.core.js +46 -6
- package/dist/bridge/bridge-adapters.opencode.js +79 -13
- package/dist/bridge/bridge-adapters.shared.js +215 -1
- package/dist/bridge/bridge-controller.js +6 -2
- package/dist/bridge/bridge-utils.js +92 -9
- package/dist/bridge/claude-hooks.js +64 -3
- package/dist/bridge/wechat-bridge.js +102 -8
- package/dist/companion/codex-remote-client.js +8 -4
- package/dist/companion/local-companion-link.js +56 -26
- package/dist/companion/local-companion-start.js +48 -5
- package/dist/companion/local-companion.js +3 -1
- package/dist/daemon/daemon-link.js +175 -0
- package/dist/daemon/wechat-daemon.js +1202 -0
- package/dist/wechat/channel-config.js +126 -33
- package/dist/wechat/standalone-bot.js +5 -6
- package/dist/wechat/wechat-channel.js +21 -10
- package/dist/wechat/wechat-transport.js +291 -9
- package/package.json +62 -98
package/README.md
CHANGED
|
@@ -2,9 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
<p align='center'><img src='docs/images/logo.png' width=90%></p>
|
|
4
4
|
|
|
5
|
+
<p align="center"><img alt="Typing SVG" src="https://readme-typing-svg.herokuapp.com?font=JetBrains+Mono&weight=600&duration=4000&pause=500&color=06C763&center=true&vCenter=true&width=660&lines=CLI+WeChat+Bridge+%E5%BE%AE%E4%BF%A1%E6%A1%A5%E6%8E%A5;%E7%9C%9F%E6%AD%A3%E7%9A%84%E2%80%9C%E7%BB%88%E7%AB%AF-%E5%BE%AE%E4%BF%A1%E2%80%9D%E5%8E%9F%E7%94%9F%E5%8F%8C%E5%90%91%E4%BA%A4%E4%BA%92"></p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/UNLINEARITY/CLI-WeChat-Bridge"><img alt="GitHub stars" src="https://img.shields.io/github/stars/UNLINEARITY/CLI-WeChat-Bridge?label=Stars&style=for-the-badge&logo=github&color=0891b2&labelColor=1c1917"></a>
|
|
9
|
+
<a href="https://www.npmjs.com/package/cli-wechat-bridge"><img alt="npm version" src="https://img.shields.io/npm/v/cli-wechat-bridge?label=npm&style=for-the-badge&logo=npm&color=cb3837&labelColor=1c1917"></a>
|
|
10
|
+
<a href="https://www.npmjs.com/package/cli-wechat-bridge"><img alt="npm downloads" src="https://img.shields.io/npm/dm/cli-wechat-bridge?label=Downloads&style=for-the-badge&logo=npm&color=16a34a&labelColor=1c1917"></a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/@unlinearity/cli-wechat-bridge"><img alt="scoped npm downloads" src="https://img.shields.io/npm/dm/@unlinearity/cli-wechat-bridge?label=Scoped%20downloads&style=for-the-badge&logo=npm&color=15803d&labelColor=1c1917"></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/cli-wechat-bridge">
|
|
13
|
+
<img alt="License" src="https://img.shields.io/npm/l/cli-wechat-bridge?label=License&style=for-the-badge&color=7c3aed&labelColor=1c1917">
|
|
14
|
+
</a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
5
17
|
**命令行工具的微信桥接**:本项目用于桥接微信消息与本地运行的 [`Codex`](https://github.com/openai/codex)、[`Claude Code`](https://code.claude.com/docs/en/overview)、`OpenCode` 或持久化 `powershell.exe` 会话,并将本地输出、审批请求与运行状态同步回微信。
|
|
6
18
|
|
|
7
|
-
|
|
19
|
+
当前实现以本地工作流为中心展开,重点是保留**本地原生终端体验**,支持原生使用以及高级参数注入,并在此基础上提供微信侧的远程输入、结果回流与状态同步能力。
|
|
8
20
|
|
|
9
21
|
<p align='center'><img src='docs/images/animation.webp' width=90%></p>
|
|
10
22
|
|
|
@@ -26,9 +38,8 @@
|
|
|
26
38
|
|
|
27
39
|
### 1. 环境要求
|
|
28
40
|
|
|
29
|
-
- Windows 为当前主要验证环境
|
|
30
41
|
- [Node.js](https://nodejs.org/en/download) `>= 24.0.0`(建议直接安装官网 LTS 版本)
|
|
31
|
-
- [Bun](https://bun.sh/docs/installation) `>= 1.0.0`
|
|
42
|
+
- [Bun](https://bun.sh/docs/installation) `>= 1.0.0` (开发者需要,普通用户无需安装)
|
|
32
43
|
- 已安装以下至少一种本地 CLI:(最好更新至最新版本)
|
|
33
44
|
- [Codex](https://github.com/openai/codex)
|
|
34
45
|
- [Claude Code](https://code.claude.com/docs/en/overview)
|
|
@@ -40,10 +51,12 @@
|
|
|
40
51
|
正式版本可以直接从 npm 安装:
|
|
41
52
|
|
|
42
53
|
```bash
|
|
43
|
-
npm install -g
|
|
54
|
+
npm install -g cli-wechat-bridge@latest
|
|
44
55
|
```
|
|
45
56
|
|
|
46
|
-
安装后可在任意项目目录中直接使用 `wechat-codex-start`、`wechat-claude-start`、`wechat-opencode-start` 等命令。
|
|
57
|
+
安装后可在任意项目目录中直接使用 `wechat-daemon`、`wechat-codex-start`、`wechat-claude-start`、`wechat-opencode-start` 等命令。
|
|
58
|
+
|
|
59
|
+
兼容说明:旧包名 `@unlinearity/cli-wechat-bridge` 会继续同步发布,已经安装旧包名的用户可以正常升级;新用户优先使用更短的 `cli-wechat-bridge`。
|
|
47
60
|
|
|
48
61
|
如果你希望从源码运行或参与开发,也可以克隆仓库并安装依赖:
|
|
49
62
|
|
|
@@ -113,14 +126,60 @@ bun run setup # 绑定微信 ClawBot
|
|
|
113
126
|
默认凭据文件路径:
|
|
114
127
|
|
|
115
128
|
```text
|
|
116
|
-
~/.
|
|
129
|
+
~/.cli-bridge/account.json
|
|
117
130
|
```
|
|
118
131
|
|
|
119
132
|
登录成功后会清理旧的 `sync_buf.txt` 和 `context_tokens.json`,避免旧会话状态污染新的登录状态。
|
|
120
133
|
|
|
134
|
+
从旧版本升级时,bridge 会自动从旧的 `~/.claude/channels/wechat` 迁移数据到 `~/.cli-bridge`,包括登录凭据、同步游标、上下文 token、工作区状态和已接收附件;已有的新目录文件不会被覆盖,旧目录也会原样保留。旧运行锁不会迁移,旧 `bridge.log` 会保存为 `legacy-bridge.log`,避免覆盖新的运行日志。
|
|
135
|
+
|
|
121
136
|
如果是首次安装或微信登录已过期,`wechat-codex-start`、`wechat-claude-start`、`wechat-opencode-start` 也会在前台提示扫码登录。
|
|
122
137
|
|
|
123
|
-
### 4.
|
|
138
|
+
### 4. 先从微信发一条同步消息(重要)
|
|
139
|
+
|
|
140
|
+
启动 bridge 后,建议先在微信里向 Bot 发送一条消息,例如 `hello`、你要执行的任务或任意一句话。这样 bridge 能拿到最新的微信会话 `context_token`,之后本地终端里的输入、最终回复和审批提示才能稳定同步回微信。
|
|
141
|
+
|
|
142
|
+
如果冷启动或长时间闲置后直接从本地终端先发消息,bridge 通常仍会捕获这条本地输入并交给 Codex / Claude Code / OpenCode 处理,但微信侧出站发送可能因为旧的 `context_token` 失效而失败。表现就是:本地已经有回复,微信暂时收不到;等你先从微信发来一条消息后,后续双向同步就能恢复正常。
|
|
143
|
+
|
|
144
|
+
### 5. 常驻 daemon 模式(推荐用于多 CLI 切换)
|
|
145
|
+
|
|
146
|
+
如果你希望微信连接长期保持在线,并在 Codex / Claude Code / OpenCode 之间来回切换,推荐在项目目录启动一个统一 daemon:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
cd D:\work\your-project
|
|
150
|
+
wechat-daemon
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
启动后,在微信里发送以下指令即可选择当前活动终端:
|
|
154
|
+
|
|
155
|
+
| 指令 | 行为 |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `/codex` | 切换到 Codex |
|
|
158
|
+
| `/claude` | 切换到 Claude Code |
|
|
159
|
+
| `/opencode` | 切换到 OpenCode |
|
|
160
|
+
|
|
161
|
+
daemon 启动后,后续切换都可以直接从微信发起;如果对应 CLI 还没有可见窗口,daemon 会自动打开或复用它,不需要再手动运行 `wechat-codex`、`wechat-claude` 或 `wechat-opencode`。
|
|
162
|
+
|
|
163
|
+
daemon 的 v1 行为是:
|
|
164
|
+
|
|
165
|
+
- daemon 绑定启动时的工作目录;暂不支持在微信里切换工作目录;
|
|
166
|
+
- 启动时会自动接管并清理旧的单 bridge 进程、失效 lock 和旧 endpoint;
|
|
167
|
+
- 切换适配器不会关闭之前的 CLI;
|
|
168
|
+
- 如果对应适配器已经有可见 CLI 在运行,则直接复用;
|
|
169
|
+
- 如果还没有对应 CLI,daemon 会自动打开一个新的可见终端;
|
|
170
|
+
- Codex / Claude / OpenCode 的重要输出都会带上 `[codex]`、`[claude]`、`[opencode]` 标签再发回微信;
|
|
171
|
+
- 可以在微信发送 `/daemon-stop` 停止 daemon。
|
|
172
|
+
|
|
173
|
+
也可以启动时直接打开某个初始 CLI:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
wechat-daemon --adapter codex
|
|
177
|
+
wechat-daemon --adapter claude --profile work
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
当同一工作目录已有 `wechat-daemon` 在运行时,`wechat-codex-start` / `wechat-claude-start` / `wechat-opencode-start` 会自动委托给 daemon:请求 daemon 切到对应 CLI,并在需要时打开可见终端,而不是停止 daemon 或关闭其他 CLI。
|
|
181
|
+
|
|
182
|
+
### 6. 单命令启动桥接(兼容模式)
|
|
124
183
|
|
|
125
184
|
先进入你要操作的项目目录:
|
|
126
185
|
|
|
@@ -139,12 +198,13 @@ cd D:\work\your-project
|
|
|
139
198
|
三者会自动完成以下动作:
|
|
140
199
|
|
|
141
200
|
1. 校验或刷新微信登录凭据;
|
|
142
|
-
2.
|
|
143
|
-
3.
|
|
144
|
-
4.
|
|
145
|
-
5.
|
|
201
|
+
2. 如果当前目录已有 `wechat-daemon`,则委托 daemon 切换到对应 CLI;
|
|
202
|
+
3. 如果没有 daemon,则复用当前目录已运行的 bridge,或在当前目录启动新的 bridge;
|
|
203
|
+
4. 如果独立 bridge 正在服务其他目录,则停止旧 bridge 并切换到当前目录;
|
|
204
|
+
5. 等待当前目录对应的本地 companion endpoint 就绪;
|
|
205
|
+
6. 打开可见的本地 CLI 会话。
|
|
146
206
|
|
|
147
|
-
|
|
207
|
+
在没有 daemon 时,`wechat-codex-start` / `wechat-claude-start` / `wechat-opencode-start` 仍按**单活工作区切换器**工作:
|
|
148
208
|
|
|
149
209
|
- 同一时间只有一个项目与微信对话;
|
|
150
210
|
- 在当前目录重复执行是幂等的;
|
|
@@ -152,7 +212,7 @@ cd D:\work\your-project
|
|
|
152
212
|
- 如果检测到可见端仍在但 worker 状态异常(如 `stopped` / `error`),会自动重启 bridge 再重新打开可见端;
|
|
153
213
|
- 在其他目录执行会显式切换活动工作区。
|
|
154
214
|
|
|
155
|
-
###
|
|
215
|
+
### 7. 手动双终端模式(调试开发)
|
|
156
216
|
|
|
157
217
|
如果你希望明确观察 bridge 与本地 CLI companion,也可以分两个终端启动。
|
|
158
218
|
|
|
@@ -168,6 +228,12 @@ cd D:\work\your-project
|
|
|
168
228
|
|
|
169
229
|

|
|
170
230
|
|
|
231
|
+
微信发来的图片和普通文件也会被接收并保存到本地数据目录:
|
|
232
|
+
|
|
233
|
+
- 保存位置:`~/.cli-bridge/inbound-attachments/<日期>/`
|
|
234
|
+
- bridge 会把本地路径追加到转发给 Codex / Claude Code / OpenCode 的 prompt 中,模型可按需读取或解析这些文件;
|
|
235
|
+
- 当前不会自动 OCR 图片,也不会自动抽取 PDF / DOCX 正文;解析动作由本地 CLI 根据路径完成。
|
|
236
|
+
|
|
171
237
|
| 适配器 | 当前状态 | 说明 |
|
|
172
238
|
| --- | --- | --- |
|
|
173
239
|
| `codex` | 已接入 | 双终端模式;本地 companion 为线程权威;微信跟随本地线程 |
|
|
@@ -223,6 +289,7 @@ OpenCode 模式下,微信侧支持 `/new` 或 `/new-session` 创建新 session
|
|
|
223
289
|
| 类型 | 命令 |
|
|
224
290
|
| --- | --- |
|
|
225
291
|
| 登录与更新 | `wechat-setup`、`wechat-check-update` |
|
|
292
|
+
| 常驻 daemon | `wechat-daemon` |
|
|
226
293
|
| Codex | `wechat-bridge-codex`、`wechat-codex`、`wechat-codex-start` |
|
|
227
294
|
| Claude Code | `wechat-bridge-claude`、`wechat-claude`、`wechat-claude-start` |
|
|
228
295
|
| OpenCode | `wechat-bridge-opencode`、`wechat-opencode`、`wechat-opencode-start` |
|
|
@@ -232,6 +299,7 @@ OpenCode 模式下,微信侧支持 `/new` 或 `/new-session` 创建新 session
|
|
|
232
299
|
|
|
233
300
|
```bash
|
|
234
301
|
bun run setup
|
|
302
|
+
bun run daemon
|
|
235
303
|
bun run bridge:codex
|
|
236
304
|
bun run codex:panel
|
|
237
305
|
bun run codex:start
|
|
@@ -247,6 +315,27 @@ bun run check
|
|
|
247
315
|
bun run test
|
|
248
316
|
```
|
|
249
317
|
|
|
318
|
+
### Daemon CLI 参数
|
|
319
|
+
|
|
320
|
+
适用于:
|
|
321
|
+
|
|
322
|
+
- `wechat-daemon`
|
|
323
|
+
|
|
324
|
+
示例:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
wechat-daemon --cwd D:\work\my-project
|
|
328
|
+
wechat-daemon --adapter codex
|
|
329
|
+
wechat-daemon --adapter claude --profile work
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
支持参数:
|
|
333
|
+
|
|
334
|
+
- `--cwd <path>`:指定 daemon 绑定的工作目录;
|
|
335
|
+
- `--adapter <codex|claude|opencode>`:启动 daemon 后立即切换到指定 CLI;
|
|
336
|
+
- `--profile <name-or-path>`:传给 daemon 创建的适配器;
|
|
337
|
+
- `--no-open`:只创建 bridge slot,不自动打开可见 CLI。
|
|
338
|
+
|
|
250
339
|
### Bridge CLI 参数
|
|
251
340
|
|
|
252
341
|
适用于:
|
|
@@ -292,16 +381,29 @@ wechat-opencode-start --cwd D:\work\my-project
|
|
|
292
381
|
- `--profile <name-or-path>`:转发给后台启动的 `wechat-bridge-codex` / `wechat-bridge-claude` / `wechat-bridge-opencode`;
|
|
293
382
|
- `--timeout-ms <ms>`:等待当前目录 endpoint 的最长时间,默认 `15000`。
|
|
294
383
|
|
|
384
|
+
高级用法:除上述启动器参数外,未知参数会继续透传给可见的底层 CLI。这样可以在保留微信登录、工作区切换与 bridge 生命周期管理的同时,启用 Codex / Claude Code 自己的高级启动模式。
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
wechat-codex-start --yolo
|
|
388
|
+
wechat-codex-start --model gpt-5.2 --yolo
|
|
389
|
+
wechat-claude-start --dangerously-skip-permissions
|
|
390
|
+
wechat-claude-start --model sonnet --dangerously-skip-permissions
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
其中 `--yolo` 会传给 Codex;`--dangerously-skip-permissions` 会传给 Claude Code。它们只影响本地可见 CLI,不会覆盖 bridge 托管的 Codex remote 连接参数、Claude settings、微信登录状态或当前工作区锁。由于这两类参数会降低审批保护,建议只在可信工作区中使用。
|
|
394
|
+
|
|
295
395
|
## 微信侧支持的指令
|
|
296
396
|
|
|
297
397
|
| 指令 | 说明 |
|
|
298
398
|
| --- | --- |
|
|
299
399
|
| 普通文本 | 发送给当前活动会话 |
|
|
400
|
+
| `/codex` / `/claude` / `/opencode` | daemon 模式下切换活动 CLI;已有 CLI 会复用,没有则自动打开 |
|
|
300
401
|
| `/status` | 查看 bridge 当前状态 |
|
|
301
402
|
| `/stop` | 中断当前任务 |
|
|
302
403
|
| `/reset` | 重建当前本地会话 |
|
|
303
404
|
| `/new` 或 `/new-session` | OpenCode 模式下新建 session |
|
|
304
405
|
| `/confirm` / `/deny` | 处理 Claude Code 权限请求;shell 等需要一次性 code 的请求会在消息中提示具体确认格式 |
|
|
406
|
+
| `/daemon-stop` | daemon 模式下停止常驻进程 |
|
|
305
407
|
|
|
306
408
|
说明:微信侧 `/resume` 目前仍保持禁用;需要切换 Codex / Claude / OpenCode 会话时,优先在本地 companion 中使用原生 `/resume`、`/new` 或对应 CLI 命令,微信会跟随本地活动会话。
|
|
307
409
|
|
|
@@ -313,15 +415,15 @@ wechat-opencode-start --cwd D:\work\my-project
|
|
|
313
415
|
- 对应的本地 companion 必须连接同一工作区;
|
|
314
416
|
- 不同工作区的状态文件相互隔离。
|
|
315
417
|
|
|
316
|
-
|
|
418
|
+
当前支持两种运行模型:
|
|
317
419
|
|
|
318
|
-
-
|
|
319
|
-
-
|
|
320
|
-
- 单活动工作区
|
|
420
|
+
- 独立 bridge:单 owner、单 bridge、单活动工作区;
|
|
421
|
+
- `wechat-daemon`:单 owner、单 daemon、绑定一个启动工作区,但可在这个工作区内同时保留 Codex / Claude Code / OpenCode 三个 CLI slot。
|
|
321
422
|
|
|
322
423
|
对 `wechat-codex-start` / `wechat-claude-start` / `wechat-opencode-start` 来说,这意味着:
|
|
323
424
|
|
|
324
|
-
-
|
|
425
|
+
- 如果同一工作区已有 daemon,它们会委托 daemon 切换 CLI;
|
|
426
|
+
- 如果没有 daemon,它们是**单活工作区切换器**;
|
|
325
427
|
- 当前目录重复执行是幂等的;
|
|
326
428
|
- 其他目录重复执行会触发工作区切换,而不是并行多开。
|
|
327
429
|
|
|
@@ -330,7 +432,7 @@ wechat-opencode-start --cwd D:\work\my-project
|
|
|
330
432
|
默认数据目录:
|
|
331
433
|
|
|
332
434
|
```text
|
|
333
|
-
~/.
|
|
435
|
+
~/.cli-bridge
|
|
334
436
|
```
|
|
335
437
|
|
|
336
438
|
主要文件如下:
|
|
@@ -342,21 +444,28 @@ wechat-opencode-start --cwd D:\work\my-project
|
|
|
342
444
|
| `context_tokens.json` | 微信上下文 token 缓存 |
|
|
343
445
|
| `bridge.log` | bridge 运行日志 |
|
|
344
446
|
| `bridge.lock.json` | bridge 运行锁 |
|
|
447
|
+
| `daemon-endpoint.json` | `wechat-daemon` 本地 IPC endpoint |
|
|
448
|
+
| `inbound-attachments/` | 微信入站图片和普通文件的本地保存目录 |
|
|
345
449
|
| `workspaces/<workspace-key>/bridge-state.json` | 当前工作区状态 |
|
|
346
450
|
| `workspaces/<workspace-key>/codex-panel-endpoint.json` | 当前工作区 companion endpoint;文件名保留历史兼容 |
|
|
451
|
+
| `workspaces/<workspace-key>/<adapter>-companion-endpoint.json` | daemon / 多适配器模式下的适配器独立 companion endpoint |
|
|
347
452
|
|
|
348
453
|
### 环境变量
|
|
349
454
|
|
|
350
455
|
| 变量名 | 说明 |
|
|
351
456
|
| --- | --- |
|
|
352
457
|
| `WECHAT_ILINK_BASE_URL` | 覆盖默认 iLink API 地址 |
|
|
353
|
-
| `
|
|
458
|
+
| `CLI_BRIDGE_DATA_DIR` | 覆盖默认数据目录 |
|
|
354
459
|
| `WECHAT_MAX_IMAGE_MB` | 覆盖图片上传大小限制,默认 20 MB |
|
|
355
460
|
| `WECHAT_MAX_FILE_MB` | 覆盖普通文件上传大小限制,默认 50 MB |
|
|
356
461
|
| `WECHAT_MAX_VOICE_MB` | 覆盖语音上传大小限制,默认 20 MB |
|
|
357
462
|
| `WECHAT_MAX_VIDEO_MB` | 覆盖视频上传大小限制,默认 100 MB |
|
|
463
|
+
| `WECHAT_MAX_INBOUND_IMAGE_MB` | 覆盖微信入站图片下载大小限制,默认 20 MB |
|
|
464
|
+
| `WECHAT_MAX_INBOUND_FILE_MB` | 覆盖微信入站普通文件下载大小限制,默认 50 MB |
|
|
358
465
|
| `WECHAT_OPENCODE_DEBUG` | 开启 OpenCode 适配器调试输出 |
|
|
359
466
|
|
|
467
|
+
旧版 `CLAUDE_WECHAT_CHANNEL_DATA_DIR` 不再作为活动数据目录配置项;如旧环境中设置过该变量,它只会被视为一次性旧数据迁移来源。新的自定义目录请使用 `CLI_BRIDGE_DATA_DIR`。
|
|
468
|
+
|
|
360
469
|
## 版本更新
|
|
361
470
|
|
|
362
471
|
### 检查更新
|
|
@@ -380,7 +489,7 @@ wechat-check-update
|
|
|
380
489
|
如果你使用 npm 全局安装,当前推荐直接执行:
|
|
381
490
|
|
|
382
491
|
```bash
|
|
383
|
-
npm install -g
|
|
492
|
+
npm install -g cli-wechat-bridge@latest
|
|
384
493
|
```
|
|
385
494
|
|
|
386
495
|
升级后请重启正在运行的 bridge 和 companion 终端,确保它们加载同一版本。
|
|
@@ -411,7 +520,7 @@ npm install -g .
|
|
|
411
520
|
请确认已经执行以下之一:
|
|
412
521
|
|
|
413
522
|
```bash
|
|
414
|
-
npm install -g
|
|
523
|
+
npm install -g cli-wechat-bridge@latest
|
|
415
524
|
npm install -g .
|
|
416
525
|
npm link
|
|
417
526
|
```
|
|
@@ -442,7 +551,7 @@ npm link
|
|
|
442
551
|
3. 检查:
|
|
443
552
|
|
|
444
553
|
```text
|
|
445
|
-
~/.
|
|
554
|
+
~/.cli-bridge/bridge.log
|
|
446
555
|
```
|
|
447
556
|
|
|
448
557
|
### 6. 本地 `/resume` 后微信不同步
|
|
@@ -456,7 +565,7 @@ npm link
|
|
|
456
565
|
如果 `bridge-state.json` 看起来正常,但微信仍然没有收到回复,优先检查:
|
|
457
566
|
|
|
458
567
|
```text
|
|
459
|
-
~/.
|
|
568
|
+
~/.cli-bridge/bridge.log
|
|
460
569
|
```
|
|
461
570
|
|
|
462
571
|
如果日志中出现 `wechat_send_failed`、`UND_ERR_CONNECT_TIMEOUT` 或 `ilinkai.weixin.qq.com:443`,通常是 bridge 到 iLink 的出站网络或代理问题,而不是本地 CLI 没有完成任务。请确认当前终端是否继承 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY`,以及 Node 是否需要 `NODE_OPTIONS=--use-env-proxy`。
|
|
@@ -465,7 +574,7 @@ npm link
|
|
|
465
574
|
|
|
466
575
|
- 微信 `/resume` 暂时被禁用(可能导致对话双向不稳定)
|
|
467
576
|
- Codex 不支持底层任务的远程审批!建议给好权限,正常使用,几乎不会主动请求较为低级的远程审批;Claude Code 支持远程审批,但是测试的不够,如有问题可以提 issue。
|
|
468
|
-
-
|
|
577
|
+
- `wechat-daemon` v1 绑定启动工作区;暂不支持从微信切换到另一个本地目录。
|
|
469
578
|
|
|
470
579
|
## 开发说明
|
|
471
580
|
|
|
@@ -474,6 +583,8 @@ npm link
|
|
|
474
583
|
| 文件 | 作用 |
|
|
475
584
|
| --- | --- |
|
|
476
585
|
| `src/bridge/wechat-bridge.ts` | bridge 主事件循环 |
|
|
586
|
+
| `src/daemon/wechat-daemon.ts` | 常驻 WeChat daemon 与多 CLI slot 管理 |
|
|
587
|
+
| `src/daemon/daemon-link.ts` | daemon 本地 IPC endpoint 与请求协议 |
|
|
477
588
|
| `src/bridge/bridge-adapters.ts` | `codex` / `claude` / `opencode` / `shell` 适配器入口 |
|
|
478
589
|
| `src/bridge/bridge-adapters.opencode.ts` | OpenCode 适配器实现 |
|
|
479
590
|
| `src/companion/local-companion.ts` | `wechat-claude` / `wechat-opencode` 本地 companion 入口 |
|
|
@@ -3,14 +3,17 @@ import net from "node:net";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { buildLocalCompanionToken } from "../companion/local-companion-link.js";
|
|
5
5
|
import { ensureWorkspaceChannelDir } from "../wechat/channel-config.js";
|
|
6
|
-
import { buildClaudeFailureMessage, buildClaudeHookScript, buildClaudeHookSettings, buildClaudePermissionDecisionHookOutput, buildClaudePermissionApprovalRequest, extractClaudeAssistantMessageText, extractClaudeResumeConversationId, extractClaudeTranscriptFinalReply, findInjectedClaudePromptIndex, normalizeClaudeAssistantMessage, parseClaudeHookPayload, } from "./claude-hooks.js";
|
|
6
|
+
import { buildClaudeFailureMessage, buildClaudeHookScript, buildClaudeHookSettings, buildClaudePermissionDecisionHookOutput, buildClaudePermissionApprovalRequest, extractClaudeAssistantMessageText, extractClaudeResumeConversationId, extractClaudeTranscriptFinalReply, findInjectedClaudePromptIndex, getClaudePermissionAutoResponse, getClaudeWechatOutboundAttachmentDenyMessage, normalizeClaudeAssistantMessage, parseClaudeHookPayload, } from "./claude-hooks.js";
|
|
7
7
|
import { detectCliApproval, normalizeOutput, nowIso, truncatePreview, } from "./bridge-utils.js";
|
|
8
8
|
import { AbstractPtyAdapter } from "./bridge-adapters.core.js";
|
|
9
9
|
import * as shared from "./bridge-adapters.shared.js";
|
|
10
|
-
const { CLAUDE_HOOK_LISTEN_HOST, CLAUDE_WECHAT_WORKING_NOTICE_DELAY_MS, DEFAULT_COLS, DEFAULT_ROWS, MODULE_DIR, buildClaudeCliArgs, isClaudeInvalidResumeError, quotePosixCommandArg, quoteWindowsCommandArg, shouldIncludeClaudeNoAltScreen, } = shared;
|
|
10
|
+
const { CLAUDE_HOOK_LISTEN_HOST, CLAUDE_WECHAT_WORKING_NOTICE_DELAY_MS, DEFAULT_COLS, DEFAULT_ROWS, MODULE_DIR, buildClaudeCliArgs, delay, isClaudeInvalidResumeError, quotePosixCommandArg, quoteWindowsCommandArg, shouldIncludeClaudeNoAltScreen, } = shared;
|
|
11
11
|
const CLAUDE_COMPACT_OUTPUT_LINE_RE = /^Compacted(?:\s*\(.*full summary.*\))?$/i;
|
|
12
12
|
const CLAUDE_COMPACT_FAILURE_RE = /Error:\s*Error during compaction:|(?:^|\b)API Error:|\b(?:compact|compaction)\s+failed\b|^Error:/i;
|
|
13
13
|
const CLAUDE_COMPACT_DEDUP_MS = 2_000;
|
|
14
|
+
const CLAUDE_BRACKETED_PASTE_START = "\u001b[200~";
|
|
15
|
+
const CLAUDE_BRACKETED_PASTE_END = "\u001b[201~";
|
|
16
|
+
const CLAUDE_REMOTE_ENTER_DELAY_MS = 40;
|
|
14
17
|
export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
|
|
15
18
|
hookServer = null;
|
|
16
19
|
hookPort = null;
|
|
@@ -99,7 +102,8 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
|
|
|
99
102
|
this.pendingCliApprovalHints = null;
|
|
100
103
|
this.clearWechatWorkingNotice(true);
|
|
101
104
|
this.setStatus("busy");
|
|
102
|
-
this.writeToPty(
|
|
105
|
+
this.writeToPty(this.buildRemoteInputPayload(text));
|
|
106
|
+
await delay(CLAUDE_REMOTE_ENTER_DELAY_MS);
|
|
103
107
|
this.writeToPty("\r");
|
|
104
108
|
this.armWechatWorkingNotice();
|
|
105
109
|
}
|
|
@@ -464,6 +468,13 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
|
|
|
464
468
|
timestamp: nowIso(),
|
|
465
469
|
});
|
|
466
470
|
}
|
|
471
|
+
buildRemoteInputPayload(text) {
|
|
472
|
+
const normalizedText = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
473
|
+
if (!normalizedText.includes("\n")) {
|
|
474
|
+
return normalizedText;
|
|
475
|
+
}
|
|
476
|
+
return `${CLAUDE_BRACKETED_PASTE_START}${normalizedText}${CLAUDE_BRACKETED_PASTE_END}`;
|
|
477
|
+
}
|
|
467
478
|
shouldTreatClaudeOutputAsCompactCompletion(text) {
|
|
468
479
|
if (this.state.status !== "busy" &&
|
|
469
480
|
this.state.status !== "awaiting_approval" &&
|
|
@@ -707,6 +718,25 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
|
|
|
707
718
|
}
|
|
708
719
|
handleClaudePermissionRequest(requestId, payload, socket) {
|
|
709
720
|
this.clearWechatWorkingNotice();
|
|
721
|
+
const denyMessage = getClaudeWechatOutboundAttachmentDenyMessage(payload);
|
|
722
|
+
if (denyMessage) {
|
|
723
|
+
this.pendingApproval = null;
|
|
724
|
+
this.state.pendingApproval = null;
|
|
725
|
+
this.state.pendingApprovalOrigin = undefined;
|
|
726
|
+
if (this.state.status === "awaiting_approval") {
|
|
727
|
+
this.setStatus("busy");
|
|
728
|
+
}
|
|
729
|
+
this.respondToClaudeHook(socket, requestId, buildClaudePermissionDecisionHookOutput("deny", denyMessage));
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const autoResponse = getClaudePermissionAutoResponse(payload);
|
|
733
|
+
if (autoResponse) {
|
|
734
|
+
if (!this.pendingApproval && !this.state.pendingApproval) {
|
|
735
|
+
this.setStatus("busy", `Claude approval auto-approved: ${truncatePreview(autoResponse.reason, 180)}`);
|
|
736
|
+
}
|
|
737
|
+
this.respondToClaudeHook(socket, requestId, buildClaudePermissionDecisionHookOutput(autoResponse.action));
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
710
740
|
this.flushPendingClaudeHookApprovals();
|
|
711
741
|
this.pendingHookApprovals.set(requestId, {
|
|
712
742
|
requestId,
|