cli-wechat-bridge 1.1.8 → 1.2.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 +21 -7
- package/bin/_run-entry.mjs +6 -1
- package/bin/wechat-check-update.mjs +6 -1
- package/bin/wechat-claude.mjs +6 -1
- package/bin/wechat-codex.mjs +6 -1
- package/bin/wechat-daemon.mjs +6 -1
- package/bin/wechat-opencode.mjs +6 -1
- package/bin/wechat-pi.mjs +6 -1
- package/bin/wechat-setup.mjs +6 -1
- package/bin/wecom-check-update.mjs +6 -1
- package/bin/wecom-claude.mjs +6 -1
- package/bin/wecom-codex.mjs +6 -1
- package/bin/wecom-daemon.mjs +6 -1
- package/bin/wecom-opencode.mjs +6 -1
- package/bin/wecom-pi.mjs +6 -1
- package/bin/wecom-setup.mjs +6 -1
- package/dist/bridge/bridge-adapters.claude.js +6 -0
- package/dist/bridge/bridge-adapters.codex.js +95 -8
- package/dist/bridge/bridge-adapters.core.js +6 -0
- package/dist/bridge/bridge-adapters.opencode.js +148 -19
- package/dist/bridge/bridge-adapters.pi.js +6 -0
- package/dist/bridge/bridge-adapters.shared.js +6 -0
- package/dist/bridge/bridge-controller.js +6 -0
- package/dist/bridge/bridge-final-reply.js +6 -0
- package/dist/bridge/bridge-process-reaper.js +6 -0
- package/dist/bridge/bridge-state.js +6 -0
- package/dist/bridge/bridge-utils.js +6 -31
- package/dist/bridge/claude-hook.js +6 -0
- package/dist/bridge/claude-native-controls.js +14 -2
- package/dist/bridge/codex-visible-thread-proxy.js +140 -0
- package/dist/bridge/native-terminal-control.js +18 -0
- package/dist/bridge/opencode-v2-compat.js +450 -0
- package/dist/bridge/wechat-bridge.js +148 -241
- package/dist/bridge/wechat-forwarding.js +6 -0
- package/dist/bridge/wechat-outbound-queue.js +6 -0
- package/dist/channels/wechat/bridge-final-reply.js +13 -22
- package/dist/channels/wechat/index.js +6 -0
- package/dist/channels/wechat/wechat-driver.js +191 -0
- package/dist/channels/wechat/wechat-forwarding.js +18 -0
- package/dist/channels/wechat/wechat-outbound-queue.js +39 -0
- package/dist/channels/wecom/bridge-final-reply.js +6 -0
- package/dist/channels/wecom/setup-cli.js +6 -0
- package/dist/channels/wecom/setup.js +6 -0
- package/dist/channels/wecom/wecom-config.js +6 -0
- package/dist/channels/wecom/wecom-driver.js +106 -0
- package/dist/channels/wecom/wecom-media.js +6 -0
- package/dist/channels/wecom/wecom-transport.js +6 -0
- package/dist/commands/check-update.js +6 -0
- package/dist/companion/codex-remote-client.js +6 -0
- package/dist/companion/codex-visible-client-link.js +6 -0
- package/dist/companion/local-companion-link.js +6 -0
- package/dist/companion/local-companion-start.js +6 -0
- package/dist/companion/local-companion.js +6 -0
- package/dist/companion/opencode-tui-bridge-plugin.js +6 -0
- package/dist/companion/opencode-v2-tui-bridge-plugin.js +93 -0
- package/dist/companion/pi-tui-bridge-extension.js +6 -0
- package/dist/core/bridge-control.js +2 -0
- package/dist/core/bridge-defer.js +25 -0
- package/dist/core/channel-driver.js +1 -0
- package/dist/core/conversation-routing.js +11 -1
- package/dist/core/direct-run.js +6 -0
- package/dist/core/inbound-message-claims.js +6 -0
- package/dist/core/outbound-target.js +32 -0
- package/dist/core/text-utils.js +6 -0
- package/dist/core/turn-coordinator.js +85 -0
- package/dist/daemon/daemon-link.js +6 -0
- package/dist/daemon/emoji-bindings.js +6 -0
- package/dist/daemon/wechat-daemon.js +451 -173
- package/dist/i18n/index.js +6 -0
- package/dist/i18n/messages-en.js +7 -1
- package/dist/i18n/messages-zh.js +7 -1
- package/dist/runtime/create-runtime-host.js +6 -0
- package/dist/utils/atomic-file.js +6 -0
- package/dist/utils/doctor.js +6 -0
- package/dist/utils/version-checker.js +6 -0
- package/dist/wechat/channel-config.js +6 -0
- package/dist/wechat/setup.js +6 -0
- package/dist/wechat/wechat-channel.js +6 -0
- package/dist/wechat/wechat-transport.js +73 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -53,10 +53,11 @@
|
|
|
53
53
|
|
|
54
54
|
- [Node.js](https://nodejs.org/en/download) `>= 22.13.0`(建议直接安装官网 LTS 版本)
|
|
55
55
|
- 已安装以下任意一种本地 CLI,并尽量保持最新版本:
|
|
56
|
-
- [Codex](https://github.com/openai/codex)
|
|
56
|
+
- [Codex](https://github.com/openai/codex)(已验证 `0.149.x`–`0.157.x`)
|
|
57
57
|
- [Claude Code](https://code.claude.com/docs/en/overview)
|
|
58
|
-
- [OpenCode](https://github.com/anomalyco/opencode)
|
|
59
|
-
- [Pi](https://github.com/earendil-works/pi)(已验证 `0.
|
|
58
|
+
- [OpenCode](https://github.com/anomalyco/opencode)(同时支持 `1.18.x` 与 `2.0.x`)
|
|
59
|
+
- [Pi](https://github.com/earendil-works/pi)(已验证 `0.85.1`;需要本机可执行 `pi` 命令)
|
|
60
|
+
- Bridge 仅调用安装在 `PATH` 中、可直接执行的独立 CLI;不会发现或调用 ChatGPT.app 等桌面应用内部捆绑的私有可执行文件。
|
|
60
61
|
- 已准备一个远程通道:个人微信使用 `wechat-setup`,企业微信使用 `wecom-setup`
|
|
61
62
|
|
|
62
63
|
### 2. 安装与更新
|
|
@@ -344,6 +345,7 @@ OpenCode 模式下,微信和企业微信侧都支持 `/new` 或 `/new-session`
|
|
|
344
345
|
| `/claude [prompt]` | daemon | 切换到 Claude Code;可选 prompt 在切换成功后转发 |
|
|
345
346
|
| `/opencode [prompt]` | daemon | 切换到 OpenCode;可选 prompt 在切换成功后转发 |
|
|
346
347
|
| `/pi [prompt]` | daemon | 切换到 Pi;可选 prompt 在切换成功后转发 |
|
|
348
|
+
| `/all <prompt>` | daemon | 将同一段提示词同时下发给全部已启动的 CLI;未启动的列为跳过,任一 CLI 忙碌时整条取消 |
|
|
347
349
|
| `/status` | 直接启动、daemon | 查看 bridge、daemon、适配器和工作区状态 |
|
|
348
350
|
| `/model`、`/model <编号>` | Codex、Claude Code、OpenCode、Pi | 列出模型并切换当前会话的模型;编号列表 5 分钟内有效 |
|
|
349
351
|
| `/plan`、`/plan on`、`/plan off` | Codex、Claude Code、OpenCode | 开启计划模式,或恢复进入前的模式 / primary agent |
|
|
@@ -365,7 +367,17 @@ OpenCode 的控制不依赖可见 TUI 的输入框、菜单或当前面板。桥
|
|
|
365
367
|
|
|
366
368
|
Pi 的 `/model` 通过已连接原生 TUI extension 读取当前 scoped model 列表;没有 scoped models 时使用 model registry 中可用的模型,并通过 `pi.setModel()` 切换。Pi 的 `/plan` 仍由可选扩展定义,不作为 bridge 的通用控制命令。
|
|
367
369
|
|
|
368
|
-
模型列表绑定当前会话、CLI 进程和操作者;切换会话或 daemon 活动 CLI 后请重新发送 `/model`。
|
|
370
|
+
模型列表绑定当前会话、CLI 进程和操作者;切换会话或 daemon 活动 CLI 后请重新发送 `/model`。
|
|
371
|
+
|
|
372
|
+
### 「正在输入」状态与回复投递
|
|
373
|
+
|
|
374
|
+
从微信派发任务后,聊天窗口会显示原生的「对方正在输入…」状态,并以 5 秒心跳保持,直到回复送达、任务失败或 daemon 关闭。typing 凭证按联系人从微信官方配置端点获取并自动刷新,获取失败时整体静默降级,不影响消息收发。
|
|
375
|
+
|
|
376
|
+
较长的回复现在作为一条完整消息发出,不再按固定长度拆分,与微信官方机器人客户端的行为一致。若服务器拒收超长消息,既有的重试与待发队列机制仍会记录失败,回复不会无声丢失。
|
|
377
|
+
|
|
378
|
+
### daemon IPC(外部程序接入)
|
|
379
|
+
|
|
380
|
+
`wechat-daemon` 与 `wecom-daemon` 在本地开放带 token 认证的 IPC 接口,外部程序(网页面板、脚本、自建机器人前端等)可以将文本按与聊天消息完全一致的处理规则下发给任意 CLI,或借用通道发送消息:忙碌时拒绝、有待审批或结构化输入时等待、Codex 本地任务期间自动排队,回复始终发回所属会话,输入被拒绝或失败时自动回滚状态。接口语义见 [docs/development.md](docs/development.md)。Claude 原生菜单名称被终端宽度截断或无法唯一识别时,桥接会提示刷新或扩大终端;OpenCode 只有在 server 回读确认后才报告切换成功。
|
|
369
381
|
|
|
370
382
|
### 5.2 会话恢复与本地同步
|
|
371
383
|
|
|
@@ -438,11 +450,13 @@ Daemon 模式支持将消息开头的表情映射为命令。表情绑定对微
|
|
|
438
450
|
|
|
439
451
|
本项目采用**双协议**授权:
|
|
440
452
|
|
|
441
|
-
**开源协议:[AGPL-3.0](LICENSE.txt)**
|
|
453
|
+
**开源协议:[AGPL-3.0-or-later](LICENSE.txt)**
|
|
442
454
|
|
|
443
455
|
- 个人使用、学习、研究:完全免费
|
|
444
|
-
- 修改和衍生作品必须以相同协议(AGPL-3.0
|
|
445
|
-
-
|
|
456
|
+
- 修改和衍生作品必须以相同协议(AGPL-3.0-or-later)开源,并保留版权声明
|
|
457
|
+
- 通过网络提供基于本项目的服务(包括仅供内部或私有部署使用的服务),也必须向服务使用者提供完整对应源代码(AGPL 第 13 条网络条款)
|
|
458
|
+
- **无论修改由人类还是 AI 工具完成**,只要修改后的版本被发布或对外提供服务,发布方都有义务以 AGPL-3.0-or-later 公开完整源码;仅自用不分发则无此义务
|
|
459
|
+
- 每个源文件头部的 SPDX 声明构成协议通知的一部分,不得删除或篡改
|
|
446
460
|
|
|
447
461
|
**商业授权**
|
|
448
462
|
|
package/bin/_run-entry.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { spawn } from "node:child_process";
|
|
4
9
|
import path from "node:path";
|
|
5
10
|
import { fileURLToPath } from "node:url";
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/commands/check-update.js", []);
|
package/bin/wechat-claude.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "claude"]);
|
package/bin/wechat-codex.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "codex"]);
|
package/bin/wechat-daemon.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/daemon/wechat-daemon.js");
|
package/bin/wechat-opencode.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "opencode"]);
|
package/bin/wechat-pi.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "pi"]);
|
package/bin/wechat-setup.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/wechat/setup.js");
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/commands/check-update.js");
|
package/bin/wecom-claude.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "claude", "--channel", "wecom"]);
|
package/bin/wecom-codex.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "codex", "--channel", "wecom"]);
|
package/bin/wecom-daemon.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/daemon/wechat-daemon.js", ["--channel", "wecom"]);
|
package/bin/wecom-opencode.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "opencode", "--channel", "wecom"]);
|
package/bin/wecom-pi.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "pi", "--channel", "wecom"]);
|
package/bin/wecom-setup.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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.
|
|
3
8
|
import { runJsEntry } from "./_run-entry.mjs";
|
|
4
9
|
|
|
5
10
|
runJsEntry("dist/channels/wecom/setup-cli.js");
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
|
|
3
|
+
// Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
|
|
4
|
+
// This file is part of CLI-WeChat-Bridge. Modifications and derivative works
|
|
5
|
+
// must be released under AGPL-3.0-or-later with full source code; see
|
|
6
|
+
// LICENSE.txt. Network services built on it must offer source to users.
|
|
1
7
|
import { NativeTerminalControl, readClaudePermissionMode } from "./native-terminal-control.js";
|
|
2
8
|
import { ClaudeNativeControls } from "./claude-native-controls.js";
|
|
3
9
|
import fs from "node:fs";
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
|
|
3
|
+
// Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
|
|
4
|
+
// This file is part of CLI-WeChat-Bridge. Modifications and derivative works
|
|
5
|
+
// must be released under AGPL-3.0-or-later with full source code; see
|
|
6
|
+
// LICENSE.txt. Network services built on it must offer source to users.
|
|
1
7
|
import fs from "node:fs";
|
|
2
8
|
import path from "node:path";
|
|
3
9
|
import crypto from "node:crypto";
|
|
@@ -6,10 +12,11 @@ import { detectCliApproval, } from "./bridge-utils.js";
|
|
|
6
12
|
import { normalizeOutput, nowIso, summarizeOutput, truncatePreview, } from "../core/text-utils.js";
|
|
7
13
|
import { AbstractPtyAdapter } from "./bridge-adapters.core.js";
|
|
8
14
|
import { killProcessTreeSync } from "./bridge-process-reaper.js";
|
|
15
|
+
import { CodexVisibleThreadProxy } from "./codex-visible-thread-proxy.js";
|
|
9
16
|
import * as shared from "./bridge-adapters.shared.js";
|
|
10
17
|
import { requestCodexVisibleClientShutdown, requestCodexVisibleThreadSwitch, } from "../companion/codex-visible-client-link.js";
|
|
11
18
|
import { readLocalCompanionEndpoint } from "../companion/local-companion-link.js";
|
|
12
|
-
import { ensureWorkspaceChannelDir } from "../wechat/channel-config.js";
|
|
19
|
+
import { appendBoundedLog as appendBridgeLog, BRIDGE_LOG_FILE, ensureWorkspaceChannelDir } from "../wechat/channel-config.js";
|
|
13
20
|
import { CODEX_REMOTE_AUTH_TOKEN_ENV, LOCAL_CLIENT_PROTOCOL_VERSION, } from "../runtime/runtime-types.js";
|
|
14
21
|
export function parseCodexCliVersion(output) {
|
|
15
22
|
const match = output.match(/\b(\d+\.\d+\.\d+)\b/);
|
|
@@ -22,7 +29,7 @@ export function isCodexVersionInCompatibilityRange(version) {
|
|
|
22
29
|
}
|
|
23
30
|
const major = parts[0] ?? -1;
|
|
24
31
|
const minor = parts[1] ?? -1;
|
|
25
|
-
return major === 0 && minor >= 149 && minor <=
|
|
32
|
+
return major === 0 && minor >= 149 && minor <= 157;
|
|
26
33
|
}
|
|
27
34
|
const { CODEX_APP_SERVER_HOST, CODEX_APP_SERVER_READY_TIMEOUT_MS, CODEX_FINAL_REPLY_SETTLE_DELAY_MS, CODEX_RECENT_SESSION_KEY_LIMIT, CODEX_RPC_CONNECT_RETRY_MS, CODEX_RPC_RECONNECT_TIMEOUT_MS, CODEX_SESSION_FALLBACK_SCAN_INTERVAL_MS, CODEX_SESSION_LOCAL_MIRROR_FALLBACK_WINDOW_MS, CODEX_SESSION_POLL_INTERVAL_MS, CODEX_STARTUP_WARMUP_MS, CODEX_THREAD_SIGNAL_TTL_MS, INTERRUPT_SETTLE_DELAY_MS, appendBoundedLog, buildCodexApprovalRequest, buildCodexCliArgs, buildCodexDynamicToolCallFailureResponse, buildCodexMcpServerElicitationDeclineResponse, buildCodexPermissionsRequestApprovalResponse, buildCodexUserInputRequest, coerceWebSocketMessageData, delay, describeUnknownError, extractCodexFinalTextFromItem, extractCodexThreadFollowIdFromStatusChanged, extractCodexThreadStartedThreadId, extractCodexUserMessageText, findCodexSessionFile, findRecentCodexSessionFileForCwd, getCodexRpcRequestId, getCodexApprovalAutoResponse, getCodexWechatOutboundAttachmentDenyMessage, getNotificationThreadId, getNotificationTurnId, isRecord, isRecentIsoTimestamp, listCodexResumeSessions, normalizeComparablePath, normalizeCodexRpcError, reserveLocalPort, resolveSpawnTarget, shouldAutoCompleteCodexWechatTurnAfterFinalReply, shouldIgnoreCodexSessionReplayEntry, shouldRecoverCodexStaleBusyState, waitForTcpPort, } = shared;
|
|
28
35
|
const CODEX_LOCAL_THREAD_ANNOUNCE_SETTLE_MS = 150;
|
|
@@ -74,6 +81,8 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
74
81
|
appServer = null;
|
|
75
82
|
nativeProcess = null;
|
|
76
83
|
appServerPort = null;
|
|
84
|
+
visibleThreadProxy = null;
|
|
85
|
+
visibleOutOfWorkspaceThreadId = null;
|
|
77
86
|
appServerShuttingDown = false;
|
|
78
87
|
appServerLog = "";
|
|
79
88
|
appServerAuthToken = null;
|
|
@@ -160,8 +169,18 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
160
169
|
this.setStatus("starting", `Codex protocol compatibility: ${compatibility}.`);
|
|
161
170
|
await this.startAppServer();
|
|
162
171
|
await this.connectRpcClient();
|
|
163
|
-
await this.restoreInitialSharedThreadIfNeeded();
|
|
164
172
|
try {
|
|
173
|
+
if (this.isHeadlessRuntimeMode() && this.appServerPort && this.appServerAuthToken) {
|
|
174
|
+
const trace = (message) => appendBridgeLog(BRIDGE_LOG_FILE, `[${nowIso()}] codex_visible_proxy: ${message}\n`);
|
|
175
|
+
this.visibleThreadProxy = await CodexVisibleThreadProxy.start({
|
|
176
|
+
upstreamUrl: `ws://${CODEX_APP_SERVER_HOST}:${this.appServerPort}`,
|
|
177
|
+
token: this.appServerAuthToken,
|
|
178
|
+
onTrace: trace,
|
|
179
|
+
onThreadOpened: (threadId, cwd) => this.handleVisibleCodexThreadOpened(threadId, cwd, trace),
|
|
180
|
+
onVisibleTurn: (threadId) => this.handleVisibleCodexTurn(threadId, trace),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
await this.restoreInitialSharedThreadIfNeeded();
|
|
165
184
|
if (this.isNativePanelMode()) {
|
|
166
185
|
await this.startNativeClient();
|
|
167
186
|
}
|
|
@@ -280,6 +299,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
280
299
|
return models;
|
|
281
300
|
}
|
|
282
301
|
async selectModel(modelId) {
|
|
302
|
+
this.assertVisibleCodexWorkspace();
|
|
283
303
|
const models = await this.listModels();
|
|
284
304
|
const selected = models.find((model) => model.id === modelId);
|
|
285
305
|
if (!selected)
|
|
@@ -288,6 +308,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
288
308
|
return selected;
|
|
289
309
|
}
|
|
290
310
|
async setPlanMode(enabled) {
|
|
311
|
+
this.assertVisibleCodexWorkspace();
|
|
291
312
|
if (!this.usesRpcTurnTransport() || !this.sharedThreadId) {
|
|
292
313
|
throw new Error("Codex plan mode requires an active app-server thread.");
|
|
293
314
|
}
|
|
@@ -458,7 +479,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
458
479
|
renderMode: "headless",
|
|
459
480
|
bridgeOwnerPid: process.pid,
|
|
460
481
|
serverPort: this.appServerPort,
|
|
461
|
-
serverUrl: `ws://${CODEX_APP_SERVER_HOST}:${this.appServerPort}`,
|
|
482
|
+
serverUrl: this.visibleThreadProxy?.url ?? `ws://${CODEX_APP_SERVER_HOST}:${this.appServerPort}`,
|
|
462
483
|
remoteAuthTokenEnv: CODEX_REMOTE_AUTH_TOKEN_ENV,
|
|
463
484
|
cwd: this.options.cwd,
|
|
464
485
|
command: this.options.command,
|
|
@@ -1014,6 +1035,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
1014
1035
|
}
|
|
1015
1036
|
}
|
|
1016
1037
|
async sendPanelTurn(text) {
|
|
1038
|
+
this.assertVisibleCodexWorkspace();
|
|
1017
1039
|
if (this.isNativePanelMode() && !this.nativeProcess) {
|
|
1018
1040
|
throw new Error("codex panel is not running.");
|
|
1019
1041
|
}
|
|
@@ -1162,6 +1184,8 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
1162
1184
|
});
|
|
1163
1185
|
this.appServer = null;
|
|
1164
1186
|
this.appServerPort = null;
|
|
1187
|
+
void this.visibleThreadProxy?.close();
|
|
1188
|
+
this.visibleThreadProxy = null;
|
|
1165
1189
|
this.appServerShuttingDown = false;
|
|
1166
1190
|
this.deleteAppServerAuthTokenFile();
|
|
1167
1191
|
this.appServerAuthToken = null;
|
|
@@ -1184,6 +1208,8 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
1184
1208
|
});
|
|
1185
1209
|
this.appServer = null;
|
|
1186
1210
|
this.appServerPort = null;
|
|
1211
|
+
void this.visibleThreadProxy?.close();
|
|
1212
|
+
this.visibleThreadProxy = null;
|
|
1187
1213
|
this.appServerShuttingDown = false;
|
|
1188
1214
|
this.deleteAppServerAuthTokenFile();
|
|
1189
1215
|
this.appServerAuthToken = null;
|
|
@@ -1654,6 +1680,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
1654
1680
|
reason: "wechat_resume",
|
|
1655
1681
|
notify: true,
|
|
1656
1682
|
});
|
|
1683
|
+
this.clearVisibleCodexWorkspaceBlock();
|
|
1657
1684
|
this.bridgeResumeReplayThreadId = targetThreadId;
|
|
1658
1685
|
this.bridgeResumeReplayUntilMs = Date.now() + CODEX_RESUME_REPLAY_SETTLE_MS;
|
|
1659
1686
|
this.setStatus("starting", `Codex resume committed thread ${targetThreadId.slice(0, 12)}.`);
|
|
@@ -1875,6 +1902,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
1875
1902
|
if (!options.preserveThread) {
|
|
1876
1903
|
this.clearPendingThreadAnnouncement();
|
|
1877
1904
|
this.announcedThreadId = null;
|
|
1905
|
+
this.visibleOutOfWorkspaceThreadId = null;
|
|
1878
1906
|
}
|
|
1879
1907
|
if (!options.preserveThread) {
|
|
1880
1908
|
this.updateSharedThread(null);
|
|
@@ -1994,6 +2022,62 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
1994
2022
|
}, CODEX_LOCAL_THREAD_ANNOUNCE_SETTLE_MS);
|
|
1995
2023
|
pending.timer.unref?.();
|
|
1996
2024
|
}
|
|
2025
|
+
handleVisibleCodexThreadOpened(threadId, cwd, trace) {
|
|
2026
|
+
if (this.pendingVisibleResume || this.shuttingDown) {
|
|
2027
|
+
trace?.(`ignored thread=${threadId.slice(0, 12)} reason=bridge_switch`);
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
if (cwd && normalizeComparablePath(cwd) !== normalizeComparablePath(this.options.cwd)) {
|
|
2031
|
+
trace?.(`paused thread=${threadId.slice(0, 12)} reason=workspace`);
|
|
2032
|
+
this.interruptWechatTurnForLocalThreadSwitch(threadId);
|
|
2033
|
+
this.clearPendingThreadAnnouncement();
|
|
2034
|
+
this.pendingThreadFollowId = null;
|
|
2035
|
+
if (this.visibleOutOfWorkspaceThreadId !== threadId) {
|
|
2036
|
+
this.visibleOutOfWorkspaceThreadId = threadId;
|
|
2037
|
+
this.emit({
|
|
2038
|
+
type: "notice",
|
|
2039
|
+
level: "warning",
|
|
2040
|
+
text: `The visible Codex terminal switched to ${cwd}, outside the bridge workspace ${this.options.cwd}. WeChat input is paused instead of being sent to the previous session. Resume a session from the bridge workspace, or restart the bridge in ${cwd}.`,
|
|
2041
|
+
timestamp: nowIso(),
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
this.clearVisibleCodexWorkspaceBlock();
|
|
2047
|
+
trace?.(`follow thread=${threadId.slice(0, 12)} source=visible_open`);
|
|
2048
|
+
this.trackLocalSharedThread(threadId, { reason: "local_follow", signal: "visible_request" });
|
|
2049
|
+
}
|
|
2050
|
+
handleVisibleCodexTurn(threadId, trace) {
|
|
2051
|
+
if (this.pendingVisibleResume || this.shuttingDown)
|
|
2052
|
+
return;
|
|
2053
|
+
if (threadId === this.sharedThreadId) {
|
|
2054
|
+
this.clearVisibleCodexWorkspaceBlock();
|
|
2055
|
+
return;
|
|
2056
|
+
}
|
|
2057
|
+
if (!findCodexSessionFile(this.options.cwd, Date.now(), { threadId })) {
|
|
2058
|
+
trace?.(`ignored thread=${threadId.slice(0, 12)} reason=workspace_or_unpersisted`);
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
this.clearVisibleCodexWorkspaceBlock();
|
|
2062
|
+
trace?.(`follow thread=${threadId.slice(0, 12)} source=visible_turn`);
|
|
2063
|
+
this.trackLocalSharedThread(threadId, { reason: "local_follow", signal: "visible_request" });
|
|
2064
|
+
}
|
|
2065
|
+
clearVisibleCodexWorkspaceBlock() {
|
|
2066
|
+
if (!this.visibleOutOfWorkspaceThreadId)
|
|
2067
|
+
return;
|
|
2068
|
+
this.visibleOutOfWorkspaceThreadId = null;
|
|
2069
|
+
this.emit({
|
|
2070
|
+
type: "notice",
|
|
2071
|
+
level: "info",
|
|
2072
|
+
text: `The visible Codex terminal is back in the bridge workspace ${this.options.cwd}. WeChat input has resumed.`,
|
|
2073
|
+
timestamp: nowIso(),
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
assertVisibleCodexWorkspace() {
|
|
2077
|
+
if (this.visibleOutOfWorkspaceThreadId) {
|
|
2078
|
+
throw new Error(`The visible Codex terminal is outside the bridge workspace ${this.options.cwd}. WeChat input is paused; resume a session from this workspace or restart the bridge in the terminal's workspace.`);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
1997
2081
|
trackLocalSharedThread(threadId, options) {
|
|
1998
2082
|
const weakSignal = options.signal === "status_changed" ||
|
|
1999
2083
|
options.signal === "thread_started" ||
|
|
@@ -2001,7 +2085,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
2001
2085
|
if (weakSignal && Date.now() < this.localThreadFollowBlockedUntilMs) {
|
|
2002
2086
|
return;
|
|
2003
2087
|
}
|
|
2004
|
-
if (options.signal === "user_message" || options.signal === "turn_started") {
|
|
2088
|
+
if (options.signal === "user_message" || options.signal === "turn_started" || options.signal === "visible_request") {
|
|
2005
2089
|
this.localThreadFollowBlockedUntilMs = 0;
|
|
2006
2090
|
}
|
|
2007
2091
|
if (this.pendingVisibleResume) {
|
|
@@ -2337,7 +2421,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
2337
2421
|
origin: "local",
|
|
2338
2422
|
};
|
|
2339
2423
|
}
|
|
2340
|
-
if (method === "turn/started" && !this.activeTurn) {
|
|
2424
|
+
if (method === "turn/started" && !this.activeTurn && !this.visibleThreadProxy) {
|
|
2341
2425
|
return {
|
|
2342
2426
|
threadId,
|
|
2343
2427
|
turnId,
|
|
@@ -2611,7 +2695,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
2611
2695
|
if (!threadId) {
|
|
2612
2696
|
return;
|
|
2613
2697
|
}
|
|
2614
|
-
if (this.pendingVisibleResume) {
|
|
2698
|
+
if (this.pendingVisibleResume || this.visibleThreadProxy) {
|
|
2615
2699
|
return;
|
|
2616
2700
|
}
|
|
2617
2701
|
if (this.isBridgeResumeReplay(threadId)) {
|
|
@@ -2690,7 +2774,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
2690
2774
|
if (!threadId) {
|
|
2691
2775
|
return;
|
|
2692
2776
|
}
|
|
2693
|
-
if (this.isBridgeResumeReplay(threadId)) {
|
|
2777
|
+
if (this.visibleThreadProxy || this.isBridgeResumeReplay(threadId)) {
|
|
2694
2778
|
return;
|
|
2695
2779
|
}
|
|
2696
2780
|
if (this.isRecentlyBridgeOwnedThread(threadId)) {
|
|
@@ -2968,6 +3052,9 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
2968
3052
|
this.rememberCompletedTurn(turnId);
|
|
2969
3053
|
}
|
|
2970
3054
|
async stopAppServer() {
|
|
3055
|
+
const visibleThreadProxy = this.visibleThreadProxy;
|
|
3056
|
+
this.visibleThreadProxy = null;
|
|
3057
|
+
await visibleThreadProxy?.close();
|
|
2971
3058
|
if (!this.appServer) {
|
|
2972
3059
|
this.appServerPort = null;
|
|
2973
3060
|
this.appServerShuttingDown = false;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
|
|
3
|
+
// Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
|
|
4
|
+
// This file is part of CLI-WeChat-Bridge. Modifications and derivative works
|
|
5
|
+
// must be released under AGPL-3.0-or-later with full source code; see
|
|
6
|
+
// LICENSE.txt. Network services built on it must offer source to users.
|
|
1
7
|
import net from "node:net";
|
|
2
8
|
import path from "node:path";
|
|
3
9
|
import { spawn as spawnPty } from "node-pty";
|