@unlinearity/cli-wechat-bridge 1.2.1 → 1.2.3

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.
Files changed (54) hide show
  1. package/README.md +146 -73
  2. package/README_EN.md +544 -0
  3. package/bin/telegram-check-update.mjs +10 -0
  4. package/bin/telegram-claude.mjs +10 -0
  5. package/bin/telegram-codex.mjs +10 -0
  6. package/bin/telegram-daemon.mjs +10 -0
  7. package/bin/telegram-opencode.mjs +10 -0
  8. package/bin/telegram-pi.mjs +10 -0
  9. package/bin/telegram-setup.mjs +10 -0
  10. package/dist/bridge/adapter-control.js +19 -12
  11. package/dist/bridge/bridge-adapters.claude.js +219 -115
  12. package/dist/bridge/bridge-adapters.codex.js +532 -99
  13. package/dist/bridge/bridge-adapters.core.js +84 -7
  14. package/dist/bridge/bridge-adapters.opencode.js +202 -55
  15. package/dist/bridge/bridge-adapters.pi.js +23 -18
  16. package/dist/bridge/bridge-adapters.shared.js +9 -9
  17. package/dist/bridge/bridge-session-resume.js +13 -6
  18. package/dist/bridge/bridge-state.js +3 -1
  19. package/dist/bridge/bridge-utils.js +94 -144
  20. package/dist/bridge/claude-hooks.js +60 -3
  21. package/dist/bridge/claude-native-controls.js +19 -12
  22. package/dist/bridge/language-control.js +33 -0
  23. package/dist/bridge/native-terminal-control.js +26 -10
  24. package/dist/bridge/opencode-v2-compat.js +11 -3
  25. package/dist/bridge/wechat-bridge.js +154 -72
  26. package/dist/channels/channel-driver-factory.js +56 -0
  27. package/dist/channels/local/local-channel-port.js +40 -0
  28. package/dist/channels/local/local-driver.js +217 -0
  29. package/dist/channels/telegram/setup-cli.js +12 -0
  30. package/dist/channels/telegram/setup.js +163 -0
  31. package/dist/channels/telegram/telegram-channel-port.js +47 -0
  32. package/dist/channels/telegram/telegram-config.js +66 -0
  33. package/dist/channels/telegram/telegram-driver.js +106 -0
  34. package/dist/channels/telegram/telegram-message.js +41 -0
  35. package/dist/channels/telegram/telegram-transport.js +378 -0
  36. package/dist/channels/wechat/wechat-driver.js +11 -2
  37. package/dist/channels/wecom/wecom-driver.js +8 -1
  38. package/dist/companion/local-companion-start.js +180 -61
  39. package/dist/companion/local-companion.js +27 -7
  40. package/dist/companion/pi-tui-bridge-extension.js +10 -4
  41. package/dist/core/bridge-control.js +3 -0
  42. package/dist/core/bridge-defer.js +3 -2
  43. package/dist/core/bridge-event-forwarder.js +3 -0
  44. package/dist/core/channel-types.js +13 -1
  45. package/dist/daemon/wechat-daemon.js +239 -126
  46. package/dist/i18n/index.js +25 -11
  47. package/dist/i18n/messages-en.js +245 -1
  48. package/dist/i18n/messages-zh.js +245 -1
  49. package/dist/runtime/legacy-adapter-runtime.js +7 -4
  50. package/dist/wechat/channel-config.js +3 -2
  51. package/package.json +14 -4
  52. package/skills/codex-bridge/SKILL.md +35 -0
  53. package/skills/codex-bridge/agents/openai.yaml +4 -0
  54. package/skills/codex-bridge/scripts/install.mjs +28 -0
package/README_EN.md ADDED
@@ -0,0 +1,544 @@
1
+ # CLI WeChat Bridge
2
+
3
+ <p align="center">
4
+ <a href="README.md">简体中文</a> · <strong>English</strong>
5
+ </p>
6
+
7
+ <p align='center'><img src='docs/images/logo.png' width=90%></p>
8
+
9
+ <p align="center"><img alt="Typing SVG" src="https://readme-typing-svg.demolab.com?font=JetBrains+Mono&weight=600&duration=4000&pause=500&color=06C763&center=true&vCenter=true&width=660&lines=CLI+WeChat+Bridge+-+AI+CLI+bridge;Native+two-way+interaction%3A+terminal+and+chat"></p>
10
+
11
+ <p align="center">
12
+ <a href="https://unlinearity.github.io/CLI-WeChat-Bridge/en/"><img alt="Product website" src="https://img.shields.io/badge/Website-Visit-0284c7?style=for-the-badge&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNyI%2BPGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iOSIvPjxlbGxpcHNlIGN4PSIxMiIgY3k9IjEyIiByeD0iNCIgcnk9IjkiLz48cGF0aCBkPSJNMyAxMmgxOCIvPjwvc3ZnPg%3D%3D&logoColor=white&labelColor=1c1917"></a>
13
+ <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>
14
+ <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>
15
+ <a href="https://www.npmjs.com/package/cli-wechat-bridge"><img alt="npm downloads" src="https://img.shields.io/npm/dt/cli-wechat-bridge?label=Downloads&style=for-the-badge&logo=npm&color=16a34a&labelColor=1c1917"></a>
16
+ <a href="https://www.npmjs.com/package/@unlinearity/cli-wechat-bridge"><img alt="scoped npm downloads" src="https://img.shields.io/npm/dt/@unlinearity/cli-wechat-bridge?label=Scoped%20downloads&style=for-the-badge&logo=npm&color=15803d&labelColor=1c1917"></a>
17
+ <a href="https://www.npmjs.com/package/cli-wechat-bridge">
18
+ <img alt="License" src="https://img.shields.io/badge/License-AGPL--3.0-7c3aed?style=for-the-badge&labelColor=1c1917">
19
+ </a>
20
+ <a href="https://github.com/UNLINEARITY/CLI-WeChat-Bridge/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/UNLINEARITY/CLI-WeChat-Bridge/ci.yml?branch=main&label=CI&style=for-the-badge&logo=githubactions&logoColor=white&labelColor=1c1917"></a>
21
+ </p>
22
+
23
+ **Bridge WeChat, WeCom, and Telegram to your local AI CLI tools**: this project connects chat messages to locally running [`Codex`](https://github.com/openai/codex), [`Claude Code`](https://code.claude.com/docs/en/overview), [`OpenCode`](https://github.com/anomalyco/opencode), and [`Pi`](https://github.com/earendil-works/pi), and sends local output, approval requests, and runtime status back to the same channel.
24
+
25
+ The project is built around local workflows and preserves the **native terminal experience**. You keep using the original CLI and its advanced options locally, while WeChat, WeCom, or Telegram provides remote input, replies, and status updates.
26
+
27
+ <p align='center'><img src='docs/images/animation.webp' width=90%></p>
28
+
29
+ ## Documentation
30
+
31
+ - [Telegram setup](#33-telegram-setup): create a bot, pair an operator, launch a CLI, or attach your current Codex session.
32
+ - [Troubleshooting](docs/troubleshooting.md) (Chinese): context tokens, network proxies, local endpoints, and known limitations.
33
+ - [Codex channel connection skill (experimental)](docs/codex-connection.md#english): use `$codex-bridge` or `--attach-current-session` to connect your current Codex session (0.162+ shared daemon mode) to WeChat, WeCom, or Telegram.
34
+ - [Runtime configuration](docs/configuration.md) (Chinese): shared data directories, interface language, upload limits, and debug environment variables.
35
+ - [Development guide](docs/development.md) (Chinese): source execution, tests, builds, packaging, and global installation smoke checks.
36
+ - [Release notes](docs/releases/README.md): changes and upgrade instructions, with English and Chinese notes.
37
+ - [Communication architecture](docs/architecture.md) (Chinese): CLI adapter protocols, PTY/RPC dependencies, and design decisions.
38
+
39
+ ## 1. What does this project solve?
40
+
41
+ This project fits workflows where:
42
+
43
+ - Most of your work happens in a local terminal.
44
+ - You want to keep using native Codex, Claude Code, OpenCode, or Pi sessions.
45
+ - You want to send requests and receive output and status through WeChat, WeCom, or Telegram while away from your computer.
46
+
47
+ The local CLI remains the primary workspace:
48
+
49
+ - Its native interface and behavior remain available.
50
+ - **WeChat, WeCom, or Telegram is the remote entry point** into that local session.
51
+ - Session consistency, thread state, and approvals stay centered on the **local session**.
52
+
53
+ ## 2. Quick start
54
+
55
+ ### 1. Requirements
56
+
57
+ - [Node.js](https://nodejs.org/en/download) `>= 22.13.0` (the official LTS release is recommended).
58
+ - Install at least one of these local CLIs and keep it up to date:
59
+ - [Codex](https://github.com/openai/codex) (managed-client workflow validated with `0.149.x`–`0.161.x`; current-session attach requires the `0.162+` shared daemon).
60
+ - [Claude Code](https://code.claude.com/docs/en/overview)
61
+ - [OpenCode](https://github.com/anomalyco/opencode) (supports both `1.18.x` and `2.0.x`).
62
+ - [Pi](https://github.com/earendil-works/pi) (connects through a native TUI extension; the `pi` command must be available locally).
63
+ - The bridge uses standalone CLI executables available in `PATH`. It does not discover private executables bundled inside desktop applications such as ChatGPT.app.
64
+ - Prepare a messaging channel: use `wechat-setup` for personal WeChat, `wecom-setup` for WeCom, or `telegram-setup` for Telegram.
65
+
66
+ ### 2. Install and update
67
+
68
+ Install or update the published npm package:
69
+
70
+ ```bash
71
+ npm install -g cli-wechat-bridge@latest
72
+ ```
73
+
74
+ The legacy package name `@unlinearity/cli-wechat-bridge` continues to receive the same releases. Existing users can keep updating it; new users should prefer the shorter `cli-wechat-bridge` name.
75
+
76
+ <details>
77
+ <summary><b>Installation issues with the node-pty native module?</b></summary>
78
+
79
+ The project uses `node-pty` for terminal emulation. **Claude Code currently uses interactive PTY mode**; when node-pty is unavailable it falls back to a compatibility mode that may not bridge correctly. Codex primarily uses WebSocket RPC and is usually unaffected. OpenCode does not depend on node-pty. Pi inherits the visible companion's real terminal and also does not need PTY emulation.
80
+
81
+ **Recent npm versions may block installation scripts on Linux.** If npm reports that the `cli-wechat-bridge` or `node-pty` install/postinstall scripts are not permitted by `allowScripts`, reinstall with explicit script permissions:
82
+
83
+ ```bash
84
+ npm uninstall -g cli-wechat-bridge
85
+ npm install -g cli-wechat-bridge@latest --allow-scripts=cli-wechat-bridge,node-pty
86
+ ```
87
+
88
+ Include `cli-wechat-bridge@latest` in the same installation command. Running `npm install -g --allow-scripts=...` without a package name makes npm look for `package.json` in the current directory and may produce `ENOENT /home/<user>/package.json`.
89
+
90
+ To keep allowing these two scripts during future global upgrades, save the setting at user level:
91
+
92
+ ```bash
93
+ npm config set allow-scripts=cli-wechat-bridge,node-pty --location=user
94
+ ```
95
+
96
+ **Linux** (the most common case): install the native module build tools:
97
+
98
+ ```bash
99
+ # Debian / Ubuntu
100
+ sudo apt install build-essential python3
101
+ # RHEL / Fedora
102
+ sudo dnf groupinstall "Development Tools" && sudo dnf install python3
103
+ # Alpine
104
+ apk add build-base python3
105
+ ```
106
+
107
+ After installing the build tools, reinstall with the `--allow-scripts=cli-wechat-bridge,node-pty` command above.
108
+
109
+ **macOS**: if compilation fails, install the Xcode command line tools with `xcode-select --install`.
110
+
111
+ **Windows**:
112
+ - Requires Windows 10 1809 (build 18309) or newer.
113
+ - If node-pty fails to load, run `npm rebuild node-pty` or reinstall.
114
+ - Install the [Visual C++ Redistributable](https://aka.ms/vs/17/release/vc_redist.x64.exe).
115
+
116
+ Run `wechat-daemon --doctor` for a quick environment check. See [PTY fallback troubleshooting](docs/troubleshooting.md#pty-不可用--回退模式) (Chinese).
117
+
118
+
119
+ </details>
120
+
121
+ ### 3. Configure your preferred channels
122
+
123
+ #### 3.1 WeChat login
124
+
125
+ After global installation, run:
126
+
127
+ ```bash
128
+ wechat-setup
129
+ ```
130
+
131
+ The QR code defaults to `small` mode. If the smaller QR characters render incorrectly in your Windows terminal, run:
132
+
133
+ ```bash
134
+ wechat-setup --qr-mode normal
135
+ ```
136
+
137
+ This selects the normal QR code mode.
138
+
139
+ The login flow:
140
+
141
+ 1. Requests a WeChat login QR code.
142
+ 2. Prints the QR code in your terminal.
143
+ 3. Waits for you to scan it and confirm in WeChat.
144
+ 4. Saves the credentials locally.
145
+
146
+ <p align="center">
147
+ <img src="docs/images/image-0.png" alt="WeChat login QR code" width="85%">
148
+ </p>
149
+
150
+ After login, the bridge clears old sync cursors and context tokens to avoid carrying stale state into the new login. See [data directories, state files, and migration](docs/troubleshooting.md#数据目录与状态文件) (Chinese).
151
+
152
+ The four direct WeChat launchers also prompt for QR login in the foreground on first use or when the login expires.
153
+
154
+ #### 3.2 WeCom setup
155
+
156
+ WeCom uses the official smart-bot persistent connection and does not need a public callback URL:
157
+
158
+ 1. In the WeCom client, create an API-mode bot under **Workbench → Smart Bot** (工作台 → 智能机器人), select the persistent connection option (使用长连接), and obtain the Bot ID and Secret.
159
+ 2. Run `wecom-setup` and enter the Bot ID and Secret.
160
+ 3. In a private chat with the bot, send the `/pair <code>` shown in your terminal to pair the operator.
161
+
162
+ <p align="center">
163
+ <img src="docs/images/image-11.png" alt="WeCom configuration" width="85%">
164
+ </p>
165
+
166
+ #### 3.3 Telegram setup
167
+
168
+ Telegram uses official [Bot API long polling](https://core.telegram.org/bots/api#getupdates), so no public callback URL is required. Your computer must be able to reach `api.telegram.org`. Telegram commands are included starting with `1.2.3`. See the [source build instructions](docs/development.md#源码更新) (Chinese).
169
+
170
+ 1. Open [@BotFather](https://t.me/BotFather) in Telegram, send `/newbot`, and follow the prompts to create a bot and obtain its Bot Token ([official instructions](https://core.telegram.org/bots/tutorial#obtain-your-bot-token)).
171
+ 2. Run `telegram-setup` in your local terminal and enter the Bot Token.
172
+ 3. Open a private chat with your new bot, click **Start**, and send `/pair <six-digit-code>` using the code shown in the terminal within 10 minutes. Use the Telegram account that will control the CLI.
173
+ 4. When the terminal prints `Telegram operator pairing completed.`, setup is complete. Credentials and operator identity are saved in `~/.cli-bridge/telegram/account.json` and reused on subsequent launches.
174
+ 5. Enter your project directory and run `telegram-daemon --adapter codex`, or choose a `telegram-*` direct launcher from the table below. Then send a task in the bot's private chat.
175
+
176
+ ```bash
177
+ telegram-setup # Configure the bot and pair its operator
178
+ telegram-setup --check # Check the configured Bot Token
179
+ ```
180
+
181
+ Run only one bridge or daemon per Bot Token at a time. Before pairing again with `telegram-setup`, stop any bridge already using that bot.
182
+
183
+ To connect your **current Codex session** to Telegram, run this from its project directory:
184
+
185
+ ```bash
186
+ telegram-codex --attach-current-session
187
+ ```
188
+
189
+ This requires Codex >= 0.162 in shared daemon mode. Success prints `Attached Codex daemon thread <id>` without opening a new visible CLI. If several live sessions share the directory, append `--codex-thread-id <id>`. See the [Codex connection guide](docs/codex-connection.md#english).
190
+
191
+ ### 4. Send a sync message from WeChat first (WeChat only)
192
+
193
+ After starting the bridge, send a message to the bot in WeChat, such as `hello`, a task, or any short text. This gives the bridge a fresh WeChat `context_token` so local terminal input, final replies, and approvals can be sent back reliably.
194
+
195
+ If you start with local terminal input after a cold start or a long idle period, the CLI may still process it, but the reply to WeChat can fail because the old `context_token` has expired. You may see a local reply without a WeChat reply. Sending a fresh message from WeChat restores context for subsequent two-way synchronization.
196
+
197
+ ### 5. Launch a local CLI directly
198
+
199
+ Enter the project directory you want to work in:
200
+
201
+ ```bash
202
+ cd D:\work\your-project
203
+ ```
204
+
205
+ Choose a launcher:
206
+
207
+ | Local CLI | WeChat command | WeCom command | Telegram command |
208
+ | --- | --- | --- | --- |
209
+ | Codex | `wechat-codex` | `wecom-codex` | `telegram-codex` |
210
+ | Claude Code | `wechat-claude` | `wecom-claude` | `telegram-claude` |
211
+ | OpenCode | `wechat-opencode` | `wecom-opencode` | `telegram-opencode` |
212
+ | Pi | `wechat-pi` | `wecom-pi` | `telegram-pi` |
213
+
214
+
215
+ Without a daemon, the four direct launchers act as a **single active workspace switcher**:
216
+
217
+ - One project at a time communicates with the selected WeChat, WeCom, or Telegram channel.
218
+ - If the visible client is running but its worker is unhealthy (for example, `stopped` or `error`), the launcher restarts the bridge and reopens the visible client.
219
+ - Launching from another directory explicitly switches the active workspace.
220
+
221
+ ### 6. Persistent daemon mode
222
+
223
+ To keep the messaging connection online and switch between Codex, Claude Code, OpenCode, and Pi, start a daemon in your project directory:
224
+
225
+ ```bash
226
+ cd D:\work\your-project
227
+ wechat-daemon
228
+ ```
229
+
230
+ WeCom uses the same daemon workflow:
231
+
232
+ ```bash
233
+ wecom-daemon --adapter claude
234
+ ```
235
+
236
+ For Telegram, use `telegram-daemon --adapter codex`. It supports the same CLI switching commands.
237
+
238
+ Send these commands in the connected channel to select the active terminal:
239
+
240
+ | Command | Behavior |
241
+ | --- | --- |
242
+ | `/codex [prompt]` | Switch to Codex; if a prompt is included, forward it immediately after switching |
243
+ | `/claude [prompt]` | Switch to Claude Code; if a prompt is included, forward it immediately after switching |
244
+ | `/opencode [prompt]` | Switch to OpenCode; if a prompt is included, forward it immediately after switching |
245
+ | `/pi [prompt]` | Switch to Pi; if a prompt is included, forward it immediately after switching |
246
+
247
+ After starting the daemon, you can switch CLIs from the messaging channel. When a CLI has no visible window, the daemon opens one or reuses an existing one.
248
+
249
+ <p align="center">
250
+ <img src="docs/images/image-9.png" alt="Multiple CLIs through WeChat" width="85%">
251
+ </p>
252
+
253
+ <p align="center">
254
+ <img src="docs/images/image-12.png" alt="Multiple CLIs through WeCom" width="85%">
255
+ </p>
256
+
257
+ <p align="center">
258
+ <img src="docs/images/image-13.png" alt="Multiple CLIs through WeCom" width="85%">
259
+ </p>
260
+
261
+ Daemon behavior:
262
+
263
+ - It stays bound to its startup working directory; switching project directories from chat is not supported.
264
+ - Startup takes over and cleans up old standalone bridge processes, stale locks, and old endpoints.
265
+ - It opens a new visible terminal when the requested CLI is not already available.
266
+ - Important output includes `[codex]`, `[claude]`, `[opencode]`, or `[pi]` labels before being sent back.
267
+ - Send `/daemon-stop` in the connected channel to stop the daemon.
268
+
269
+ You can also select the initial CLI when starting it:
270
+
271
+ ```bash
272
+ wechat-daemon --adapter codex
273
+ wechat-daemon --adapter claude --profile work
274
+ ```
275
+
276
+ When a daemon for the same channel is already running in the same directory, direct launchers delegate to it. They select the requested CLI and open its visible terminal when needed while keeping the daemon and other CLI slots running.
277
+
278
+ ## 3. Adapter support
279
+
280
+ > Local files can be sent to WeChat, WeCom, or Telegram, and files received from these channels can be passed to the local CLI for analysis. The selected model and CLI must support the file type.
281
+
282
+ <p align="center">
283
+ <img src="docs/images/image-8.png" alt="File transfer" width="85%">
284
+ </p>
285
+
286
+ Images and ordinary files received through WeChat, WeCom, or Telegram are saved locally:
287
+
288
+ - The bridge adds local file paths to the prompt forwarded to Codex, Claude Code, OpenCode, or Pi, so the CLI can read or analyze them.
289
+ - The bridge does not perform OCR or extract PDF/DOCX text automatically. The local CLI handles parsing through those paths.
290
+ - Telegram attachments are saved under `~/.cli-bridge/telegram/inbound-attachments/`; videos, voice messages, and audio files are also supported. See [data directories and state files](docs/troubleshooting.md#数据目录与状态文件) (Chinese) for other channels.
291
+
292
+ | Adapter | Status | Notes |
293
+ | --- | --- | --- |
294
+ | `codex` | Supported | `wechat-codex` / `wecom-codex` / `telegram-codex` ensure the internal runtime and open a visible Codex client; the channel follows its local thread |
295
+ | `claude` | Supported | `wechat-claude` / `wecom-claude` / `telegram-claude` start or reuse Claude Code; sessions, final replies, and approvals follow Claude session semantics |
296
+ | `opencode` | Supported | `wechat-opencode` / `wecom-opencode` / `telegram-opencode` start or reuse OpenCode; local session following and remote `/new` / `/new-session` are supported |
297
+ | `pi` | Supported | `wechat-pi` / `wecom-pi` / `telegram-pi` launch the native Pi TUI and connect the messaging channel to the same session through a local extension; final replies, stopping, new sessions, and restoring are supported |
298
+
299
+ Pi runs with the local user's permissions. The bridge adds no tool approval layer and passes `--approve` to trust project configuration, extensions, and other local files; this flag is not a per-tool approval switch. File access and commands use the account that started the Pi launcher. Native themes, shortcuts, model selection, and extension UI remain available. `wechat-pi`, `wecom-pi`, and `telegram-pi` are themselves the native Pi TUI controlled through the channel; do not start a second Pi process writing to the same session file. Pi `0.85.0` and later require Node.js `>=22.19.0`; all three launchers check this before startup.
300
+
301
+ ### Codex examples
302
+
303
+ <p align="center">
304
+ <img src="docs/images/image-3.png" alt="Codex windows" width="85%">
305
+ </p>
306
+
307
+ <p align="center">
308
+ <img src="docs/images/image-4.png" alt="Codex Linux" width="85%">
309
+ </p>
310
+
311
+ ### Claude Code examples
312
+
313
+ <p align="center">
314
+ <img src="docs/images/image-6.png" alt="Claude Windows" width="85%">
315
+ </p>
316
+
317
+ <p align="center">
318
+ <img src="docs/images/image-7.png" alt="Claude Linux" width="85%">
319
+ </p>
320
+
321
+ ### OpenCode / Pi examples
322
+
323
+ In OpenCode mode, all three channels support `/new` or `/new-session`. A new session created in the local OpenCode CLI also becomes the session followed by the channel.
324
+
325
+ ## 4. Terminal commands
326
+
327
+ Run commands from the target project directory. `wechat-*` selects personal WeChat, `wecom-*` selects WeCom, and `telegram-*` selects Telegram. The adapter names and options are shared across all three.
328
+
329
+ ### 4.1 Global entry points
330
+
331
+ | Purpose | WeChat | WeCom | Telegram | Notes |
332
+ | --- | --- | --- | --- | --- |
333
+ | Login / pairing | `wechat-setup` | `wecom-setup` | `telegram-setup` | WeChat uses QR login; WeCom takes a Bot ID and Secret; Telegram takes a Bot Token. WeCom and Telegram require `/pair` |
334
+ | Persistent daemon | `wechat-daemon` | `wecom-daemon` | `telegram-daemon` | Keep a channel connection alive and manage multiple CLI slots in the current directory |
335
+ | Codex | `wechat-codex` | `wecom-codex` | `telegram-codex` | Start or reuse a visible Codex terminal |
336
+ | Claude Code | `wechat-claude` | `wecom-claude` | `telegram-claude` | Start or reuse a visible Claude Code terminal |
337
+ | OpenCode | `wechat-opencode` | `wecom-opencode` | `telegram-opencode` | Start or reuse a visible OpenCode terminal |
338
+ | Pi | `wechat-pi` | `wecom-pi` | `telegram-pi` | Start or reuse the native Pi TUI |
339
+
340
+ The deprecated `wechat-*-start` aliases were removed in 1.1.5. Use the entry points above. Public `wechat-bridge*` commands and the Shell adapter have also been removed; direct launchers and daemons manage the internal bridge runtime.
341
+
342
+ ### 4.2 Persistent daemon
343
+
344
+ | Option | Purpose | Example |
345
+ | --- | --- | --- |
346
+ | `--cwd <path>` | Bind the daemon to a working directory | `wechat-daemon --cwd D:\work\my-project` |
347
+ | `--adapter <codex / claude / opencode / pi>` | Select the initial CLI | `wechat-daemon --adapter claude` |
348
+ | `--profile <name-or-path>` | Pass a profile to the adapter | `wechat-daemon --adapter claude --profile work` |
349
+ | `--no-open` | Create a runtime slot without opening a visible CLI | `wechat-daemon --no-open` |
350
+ | `--doctor` | Check Node.js, CLIs, locks, endpoints, and daemon state | `wechat-daemon --doctor` |
351
+
352
+ WeCom and Telegram daemons accept the same options, for example `wecom-daemon --adapter claude --profile work` or `telegram-daemon --adapter claude --profile work`. The daemon remains bound to its startup directory. See the [channel commands](#5-channel-commands) below.
353
+
354
+ ### 4.3 Direct adapter launchers
355
+
356
+ | Adapter | WeChat | WeCom | Telegram | Default session policy |
357
+ | --- | --- | --- | --- | --- |
358
+ | Codex | `wechat-codex` | `wecom-codex` | `telegram-codex` | New session |
359
+ | Claude Code | `wechat-claude` | `wecom-claude` | `telegram-claude` | New session |
360
+ | OpenCode | `wechat-opencode` | `wecom-opencode` | `telegram-opencode` | New session |
361
+ | Pi | `wechat-pi` | `wecom-pi` | `telegram-pi` | New session |
362
+
363
+ Shared options:
364
+
365
+ | Option | Purpose | Example |
366
+ | --- | --- | --- |
367
+ | `--cwd <path>` | Set the runtime and visible CLI working directory | `wechat-claude --cwd D:\work\my-project` |
368
+ | `--profile <name-or-path>` | Pass a profile to the internal runtime | `wechat-claude --profile work` |
369
+ | `--timeout-ms <ms>` | Endpoint readiness timeout, default `15000` ms | `wechat-codex --timeout-ms 30000` |
370
+ | `--session-start-mode <restore / new>` | Explicitly restore a session or create a new one | `wechat-pi --session-start-mode restore` |
371
+ | `--attach-current-session` | Codex only: attach the selected channel to a live shared-daemon session in this directory without opening a visible CLI; requires Codex >= 0.162 | `wechat-codex --attach-current-session` |
372
+ | `--codex-thread-id <id>` | Codex only: select the exact thread for attachment instead of automatic directory discovery | `wechat-codex --attach-current-session --codex-thread-id <id>` |
373
+ | `--doctor` | Check the selected adapter and workspace without starting a CLI | `wechat-claude --doctor` |
374
+ | Other arguments | Forward to the underlying visible CLI | `wechat-codex --model gpt-5.2 --yolo` |
375
+
376
+ `--yolo` is forwarded to Codex; `--dangerously-skip-permissions` is forwarded to Claude Code. These options affect the visible CLI without overriding internal runtime settings, channel credentials, or workspace locks. Change the command prefix to `wecom-` or `telegram-` for the other channels.
377
+
378
+ ### 4.4 Updates
379
+
380
+ | Action | Command | Notes |
381
+ | --- | --- | --- |
382
+ | Check for updates | `wechat-check-update` / `wecom-check-update` / `telegram-check-update` | Query the latest npm version |
383
+ | Upgrade | `npm install -g cli-wechat-bridge@latest` | Restart active bridges, daemons, and companions after upgrading |
384
+
385
+ Direct launchers also accept `--doctor` to check the selected adapter and current workspace without starting the CLI.
386
+
387
+ ## 5. Channel commands
388
+
389
+ These commands work with WeChat, WeCom, and Telegram. Ordinary text is sent to the active session. Adapter switching commands with `[prompt]` first select the CLI, then forward the prompt.
390
+
391
+ ### 5.1 Shared controls
392
+
393
+ | Command | Scope | Behavior |
394
+ | --- | --- | --- |
395
+ | Ordinary text | Direct launch, daemon | Send to the active session |
396
+ | `/codex [prompt]` | Daemon | Switch to Codex and optionally forward a prompt |
397
+ | `/claude [prompt]` | Daemon | Switch to Claude Code and optionally forward a prompt |
398
+ | `/opencode [prompt]` | Daemon | Switch to OpenCode and optionally forward a prompt |
399
+ | `/pi [prompt]` | Daemon | Switch to Pi and optionally forward a prompt |
400
+ | `/all <prompt>` | Daemon | Send the same prompt to all started CLIs; unstarted slots are skipped, and the whole dispatch is canceled if any CLI is busy |
401
+ | `/status` | Direct launch, daemon | Show bridge, daemon, adapter, and workspace state |
402
+ | `/lang`, `/lang zh`, `/lang en` | Direct launch, daemon | View or switch Chinese/English bridge messages; applies immediately and saves the preference. `/language` is an alias |
403
+ | `/model`, `/model <number>` | Codex, Claude Code, OpenCode, Pi | List and switch models for the current session; numbered lists expire after 5 minutes |
404
+ | `/plan`, `/plan on`, `/plan off` | Codex, Claude Code, OpenCode | Enable plan mode or restore the previous mode / primary agent |
405
+ | `/stop` | Direct launch, daemon | Interrupt the current task |
406
+ | `/reset` | Direct launch, daemon | Rebuild the local session |
407
+ | `/new`, `/new-session` | OpenCode, Pi | Create a new session |
408
+ | `/resume`, `/resume <number or ID prefix>` | Codex, Claude Code, OpenCode, Pi | List and restore recent threads/sessions in the current directory; numbered lists expire after 5 minutes |
409
+ | `/confirm`, `/yes` | Direct launch, daemon | Approve the current pending permission request |
410
+ | `/deny`, `/no` | Direct launch, daemon | Reject the current pending permission request |
411
+ | `confirm`, `yes` | Pending approval | Text aliases for `/confirm` |
412
+ | `deny`, `no` | Pending approval | Text aliases for `/deny` |
413
+ | `/answer <key>=<value>` | Pending question | Submit structured answers for Codex, Claude Code, or OpenCode |
414
+ | `/daemon-stop` | Daemon | Stop the persistent daemon |
415
+ | `/pair <code>` | WeCom / Telegram setup | Pair the operator after `wecom-setup` or `telegram-setup` prompts you |
416
+
417
+ Send `/lang en` in the connected chat for English bridge messages, or `/lang zh` for Chinese. For startup environment-variable precedence, see [interface language](docs/configuration.md#界面语言) (Chinese).
418
+
419
+ Claude's `AskUserQuestion` forwards questions, option descriptions, and text previews. For one question, reply with `/answer 1` or custom text. For multiple questions, use `/answer 1=Yes;2=2;3=1,2;4=1`; separate multiple selections with commas and answer all questions in one submission. When the hook times out or disconnects, the remote question is cleared and you are prompted to answer locally or use `/stop`. Restart the daemon and visible Claude window after upgrading.
420
+
421
+ Claude Code model and plan controls require the visible CLI to be at an idle native prompt with no pending approvals, local draft, or popup. The bridge briefly opens the native selector and waits for confirmation; local keyboard input cancels the remote control operation. Model changes use the selector's `s` option (current session only), which must be supported by the CLI. `/plan off` restores the previous permission mode or `default` if no previous mode was recorded.
422
+
423
+ OpenCode controls use the server's session API independently of the visible TUI's input field, menus, or panels. The bridge queries connected models, submits the model and primary agent, and reads the session back to verify the change. Input from all three channels goes through the SDK with the effective agent, model, and variant. `/plan` selects the `plan` primary agent and reports an error if it is unavailable; `/plan off` restores the previous agent, or `build` / the first non-plan primary agent when no history exists.
424
+
425
+ Codex, Claude Code, and OpenCode approvals are handled in arrival order. Each `/confirm` or `/deny` resolves only the currently displayed request, then shows the next one. Requests resolved in the local terminal are removed remotely too. Subagent permissions remain attached to their parent task; approval does not switch sessions or change the reply target. Restart existing CLI windows after upgrading: older companions may still report status but require a restart for remote approvals.
426
+
427
+ By default, Claude Code launch settings include `WebFetch(domain:*)` to allow ordinary web requests. Existing deny rules and sandbox restrictions still apply. Set `CLI_BRIDGE_STRICT_APPROVAL=1` to disable this automatic allowance. Network prompts that appear only in the local terminal without a `PermissionRequest` hook must be handled locally.
428
+
429
+ Pi's `/model` reads scoped models from the connected native TUI extension. When no scoped models exist, it uses models available in the model registry and calls `pi.setModel()` to switch. Pi's `/plan` remains defined by optional extensions and is not a generic bridge control.
430
+
431
+ Model lists are bound to the session, CLI process, and operator. Send `/model` again after changing the session or active daemon CLI.
432
+
433
+ ### Typing indicators and reply delivery
434
+
435
+ Tasks dispatched from WeChat show its native typing indicator, refreshed every 5 seconds until delivery, task failure, or daemon shutdown. Typing credentials are fetched per recipient from the official WeChat configuration endpoint and refreshed automatically. If fetching fails, typing indicators are silently disabled without affecting messages.
436
+
437
+ Long WeChat replies are sent as a complete message rather than split at a fixed length. If the server rejects an oversized reply, the existing retry and pending-message mechanisms record the failure so it is not silently lost.
438
+
439
+ Telegram tasks also keep the native typing indicator active, and long replies are split to fit Telegram's message length limits.
440
+
441
+ ### Daemon IPC for external integrations
442
+
443
+ `wechat-daemon`, `wecom-daemon`, and `telegram-daemon` expose a local IPC interface with token authentication. External applications such as web panels, scripts, or custom bot frontends can dispatch input using the same rules as chat messages or send through the channel. Busy dispatches are rejected, pending approvals and questions pause dispatch, and input during a local Codex task is queued automatically. Replies retain their conversation target, and rejected or failed input rolls back state. See [development documentation](docs/development.md) (Chinese) for the interface. If Claude menu labels are truncated by terminal width or cannot be identified uniquely, the bridge asks you to refresh or enlarge the terminal. OpenCode reports a successful switch only after server readback confirms it.
444
+
445
+ ### 5.2 Session restoration and local synchronization
446
+
447
+ | Adapter | Remote `/resume` | Local synchronization |
448
+ | --- | --- | --- |
449
+ | Codex | List CLI / VS Code root threads in the current directory, preflight with `thread/read` and `thread/resume`, then commit the shared thread once the visible client is stable | Codex picker or local `/resume` thread changes are synchronized to the channel |
450
+ | Claude Code | Submit an exact `/resume <UUID>` to the current PTY and wait for `SessionEnd(reason=resume)` and target `SessionStart(source=resume)` | Local picker or `/resume` changes produce the same hook sequence; the channel follows the new transcript |
451
+ | OpenCode | List sessions in the current directory and restore the selected session | Local picker, `/resume`, or a new session updates the session followed by the channel |
452
+ | Pi | List sessions in the current directory and restore the selected session | Local session changes are followed; each completed Pi turn sends one final reply |
453
+
454
+ All adapters' `/resume` commands operate on recent root threads/sessions in the bridge or daemon's startup directory and wait for the current task to become idle. Resolve active tasks, approvals, or questions first, or use `/stop`. Active Codex threads and running Claude background sessions cannot be taken over remotely. Local OpenCode and Pi session changes invalidate earlier numbered lists. If a local switch happens during a remote task, the bridge interrupts the old task first.
455
+
456
+ ### 5.3 Daemon emoji bindings
457
+
458
+ Daemon mode maps emoji tokens at the start of a message to commands. All three channels share the configuration, saved in `~/.cli-bridge/emoji-bindings.json`; edits apply immediately.
459
+
460
+ The Chinese tokens below are literal bindings. Type them as shown, or customize them with `/bind`.
461
+
462
+ <p align="center">
463
+ <img src="docs/images/image-10.png" alt="Emoji command triggers" width="85%">
464
+ </p>
465
+
466
+ | Emoji token | Default command | Behavior |
467
+ | --- | --- | --- |
468
+ | `[OK]` | `/confirm` | Approve a permission request |
469
+ | `[闭嘴]` | `/stop` | Interrupt the current task |
470
+ | `[拥抱]` | `/claude` | Switch to Claude Code |
471
+ | `[强]` | `/codex` | Switch to Codex |
472
+ | `[胜利]` | `/opencode` | Switch to OpenCode |
473
+ | `[再见]` | `/daemon-stop` | Stop the daemon |
474
+
475
+ | Management command | Behavior |
476
+ | --- | --- |
477
+ | `/bindings` | Show all bindings |
478
+ | `/bind [emoji] /command` | Bind an emoji token, for example `/bind [微笑] /status` |
479
+ | `/unbind [emoji]` | Remove a binding, for example `/unbind [微笑]` |
480
+
481
+ Tokens must appear at the start of the message to trigger a binding. Text after the token is forwarded after the command executes. Matching is case insensitive.
482
+
483
+ ## 6. Acknowledgments and license
484
+
485
+ ### Thank you
486
+
487
+ > Thanks to everyone who reports issues or contributes pull requests.
488
+
489
+ If this project helps you, you can support its development by buying the author a milk tea. ❤️
490
+
491
+ <p align='center'><img src='docs/images/wechat-tip.png' width=60%></p>
492
+
493
+ ### Related links
494
+
495
+ <p align="center">
496
+ <a href="https://atomgit.com/UNLINEARITY/CLI-WeChat-Bridge"><img alt="AtomGit G-Star" src="https://atomgit.com/UNLINEARITY/CLI-WeChat-Bridge/star/new_badge.svg"></a><br>
497
+ This project is also hosted on AtomGit.
498
+ </p>
499
+
500
+
501
+ Main dependencies:
502
+
503
+ - [@opencode-ai/sdk](https://www.npmjs.com/package/@opencode-ai/sdk): OpenCode session and event stream client.
504
+ - [@wecom/aibot-node-sdk](https://www.npmjs.com/package/@wecom/aibot-node-sdk): WeCom smart-bot WebSocket client.
505
+ - [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk): TypeScript SDK used by the MCP server entry point.
506
+ - [node-pty](https://github.com/microsoft/node-pty): local PTY / ConPTY process bridge.
507
+ - [qrcode-terminal](https://github.com/gtanner/qrcode-terminal): terminal QR code rendering.
508
+
509
+ Runtime and development tools:
510
+
511
+ - [Node.js](https://nodejs.org/): published packages and CLI entry points.
512
+ - [TypeScript](https://www.typescriptlang.org/): source language and build tooling.
513
+ - [Bun](https://bun.sh/): source execution and tests.
514
+ - [ESLint](https://eslint.org/): code checks.
515
+
516
+ Community and references:
517
+
518
+ - [Linux DO](https://linux.do/): an AI and technology community.
519
+ - [@tencent-weixin/openclaw-weixin](https://www.npmjs.com/package/@tencent-weixin/openclaw-weixin): the OpenClaw Weixin channel npm package published by Tencent's WeChat team.
520
+ - [openclaw-weixin](https://github.com/hao-ji-xing/openclaw-weixin): an early WeChat / Claude Code Channel reference project.
521
+
522
+ ### License
523
+
524
+ This project is **dual licensed**:
525
+
526
+ **Open source: [AGPL-3.0-or-later](LICENSE.txt)**
527
+
528
+ - Free for personal use, study, and research.
529
+ - Modifications and derivative works must be released under AGPL-3.0-or-later and retain the copyright notices.
530
+ - If you provide a service based on this project over a network, including internal or private deployments, you must offer users the complete corresponding source code (AGPL section 13).
531
+ - **Whether changes are made by people or AI tools**, distributing a modified version or providing it as a network service requires releasing the complete corresponding source under AGPL-3.0-or-later. Private use without distribution carries no such obligation.
532
+ - Source-file SPDX notices are part of the license notices and must not be removed or altered.
533
+
534
+ **Commercial licensing**
535
+
536
+ If you want to use this project in a closed-source commercial product without disclosing your source code, you need a commercial license. Contact the author to discuss licensing:
537
+
538
+ - GitHub: [@UNLINEARITY](https://github.com/UNLINEARITY)
539
+
540
+ <!-- star-history:start -->
541
+ <p align="center">
542
+ <a href="https://star-history.com/#UNLINEARITY/CLI-WeChat-Bridge&amp;Date"><img src="https://raw.githubusercontent.com/UNLINEARITY/CLI-WeChat-Bridge/main/assets/star-history/star-history.png" alt="Star History" width="85%"></a>
543
+ </p>
544
+ <!-- star-history:end -->
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/commands/check-update.js", ["--channel", "telegram"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "claude", "--channel", "telegram"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "codex", "--channel", "telegram"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/daemon/wechat-daemon.js", ["--channel", "telegram"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "opencode", "--channel", "telegram"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "pi", "--channel", "telegram"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/channels/telegram/setup-cli.js");