OpenCode + wechat-acp + WeChat 机器人集成工作区。
work/
├── src/
│ └── utils.js # 纯函数工具库(测试入口)
├── wechat-adapter.js # WeChat 机器人适配器(ACP Agent)
├── start-all.bat # 一键启动所有服务
├── stop-all.bat # 停止所有服务
├── start-webui.bat # 启动 OpenChamber Web UI(自动拉起 4096 后端)
├── stop-webui.bat # 停止 OpenChamber Web UI
├── restart-wechat.bat # 重启微信机器人(修改 wechat-adapter.js 后使用)
├── wechat-bridge.bat # 启动 wechat-acp 守护进程
├── setup.bat # 环境安装与版本锁定脚本
├── run_del_nul.bat # 运行任务(删除 nul 文件后执行)
├── del.py # 删除当前目录下的 nul 文件
├── .tool-versions.json # 版本锁定文件(所有工具/仓库的锁定版本)
├── .wechat-session.json # 当前选中的 OpenCode 会话 ID
├── .wechat-subscribers.json # 微信订阅者列表(含静音状态)
├── .wechat-workspaces.json # 工作区预设列表
├── .wechat-workspace-current.json # 当前选中的工作区
├── .wechat-adapter.log # 运行时日志(超 200MB 自动截断至 100MB)
├── Dockerfile # Docker 镜像构建(wechat-bot)
├── docker-compose.yml # Docker 编排(opencode-server + wechat-bot)
├── .dockerignore # Docker 构建忽略规则
├── .github/workflows/ # GitHub Actions CI/CD 工作流
├── package.json # Node.js 依赖
├── vitest.config.js # Vitest 测试配置
├── tests/
│ ├── setup.js # 全局 mock 配置
│ ├── unit/utils/
│ │ ├── helpers.test.js # 核心纯函数测试(17 函数)
│ │ ├── edge-cases.test.js # null/undefined/边界值测试
│ │ ├── question.test.js # formatQuestionBody 测试
│ │ └── notification.test.js # 通知消息格式测试
│ ├── unit/command/ # 预留:命令处理单元测试
│ └── integration/
│ └── event-format.test.js # eventToNotification 集成测试
└── README.md
| 服务 | 端口 | 说明 |
|---|---|---|
| OpenChamber | 2048 | Web UI,连接 4096 后端(Basic Auth) |
| OpenCode API | 4096 | 官方 headless server(仅 API,无自带 UI) |
| WeChat bot | — | 微信机器人,共享会话 |
OpenChamber:功能更全、更稳定的第三方开源 Web UI,替代 opencode 官方自带 UI 与旧第三方 pk-opencode-webui。默认连接本机 4096 后端,免去
opencode web内置界面。通过npm i -g @openchamber/web安装(版本锁定见.tool-versions.json)。
所有工具和依赖的版本由 .tool-versions.json 统一管理:
| 类别 | 锁定项 | 说明 |
|---|---|---|
| 运行时 | Node.js、npm | 安装后比对版本,不匹配输出 [WARN] |
| Shell | PowerShell 7+ | 同上 |
| 语言 | Python | 同上 |
| VCS | Git | 同上 |
| npm 包 | opencode CLI | 未安装时装锁定版本,已安装版本不匹配则 warn |
| npm 包 | OpenChamber CLI | 同上(@openchamber/web) |
| npx 包 | wechat-acp | 始终下载锁定版本(npx wechat-acp@<版本>) |
首次使用或遇到依赖问题时,运行:
setup.bat脚本自动读取 .tool-versions.json 中的版本锁,检查/安装:Node.js、opencode CLI(锁定版本)、OpenChamber CLI(锁定版本)、npm 依赖,刷新 wechat-acp(锁定版本)。
PowerShell 执行策略:Windows 默认
Restricted会禁止运行.ps1脚本,导致版本锁加载失败。setup.bat 会先检测执行策略,受限时提示执行Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned解除后重新运行(仅对当前用户生效,无需管理员)。
替代本地环境安装,使用 Docker 容器化运行(推荐用于生产环境):
docker compose build wechat-bot # 构建 wechat-bot 镜像
docker compose up -d # 启动所有服务
docker compose down # 停止服务
docker compose logs -f # 查看实时日志| 服务 | 镜像 | 大小 | 端口 | 说明 |
|---|---|---|---|---|
opencode-server |
ghcr.io/anomalyco/opencode(官方) |
247 MB | 4096 | OpenCode headless server(API) |
wechat-bot |
本地构建 | 487 MB | — | wechat-acp + wechat-adapter.js |
OpenChamber Web UI 在容器外本地运行(
npm i -g @openchamber/web),连接容器暴露的 4096 端口即可。
使用命名卷保留运行时数据,重启/重建不丢失:
| 卷 | 挂载点 | 服务 | 说明 |
|---|---|---|---|
opencode-config |
/root/.config/opencode |
opencode-server | 配置、provider 凭据 |
opencode-data |
/root/.local/share/opencode |
opencode-server | opencode.db(会话/对话历史)、日志、仓库 |
project-data |
/app |
wechat-bot | 项目文件、状态文件(.wechat-session.json 等) |
wechat-acp-config |
/home/appuser/.wechat-acp |
wechat-bot | WeChat 登录凭据(扫码一次后持久有效) |
更新
wechat-adapter.js后需重建镜像并重启:docker compose build wechat-bot && docker compose up -d
打 v* tag 推送后,GitHub Actions 自动执行:
- 构建
wechat-bot镜像并推送到ghcr.io - 导出
.tar.gz离线包 - 创建 Release 并上传附件
# 手动触发(不创建 Release)
# 在 GitHub → Actions → Build Docker Images → Run workflow
# 打 tag 触发完整流程
git tag v1.2.4
git push origin v1.2.4拉取已构建的镜像:
docker pull ghcr.io/happymy/opencode-quickstart-workspace/wechat-bot:v1.2.4wechat-acp 首次运行或登录过期时,会在终端打印彩色二维码,需要用微信扫码登录。Docker 中操作如下:
# 1. 启动所有服务
docker compose up -d
# 2. 查看 wechat-bot 容器名(通常是 work-wechat-bot-1)
docker compose ps
# 3. 附加到容器终端查看二维码
docker attach work-wechat-bot-1
# 此时终端会显示 wechat-acp 的彩色二维码(██████ 块状图案)
# 打开手机微信扫一扫,扫码登录
# 登录成功后按 Ctrl+P 然后 Ctrl+Q 安全脱离(容器继续运行)登录状态保存在 wechat-acp-config 卷中,后续重启无需重新扫码。如需强制重新登录:
docker compose exec wechat-bot wechat-acp --login不要用
docker compose logs查看二维码——logs 会丢失 ANSI 颜色码,二维码不可读。
start-all.bat或单独启动:
start-webui.bat # OpenChamber Web UI(自动拉起 4096 后端)
wechat-bridge.bat # WeChat 机器人系统 Web 界面由 OpenChamber 提供,与 TUI/微信共享同一份会话数据。相比 opencode 官方自带 UI 与旧第三方 pk-opencode-webui,OpenChamber 功能更全(会话管理、AI 对话、diff 审查、权限审批、文件浏览、模型切换等)、bug 更少、更稳定。
- 地址:http://localhost:2048
- 自动连接本机 4096 后端(
OPENCODE_PORT=4096 OPENCODE_SKIP_START=true) - 功能:会话管理、AI 对话、diff 审查、权限审批、文件查看等
- 安装:
npm i -g @openchamber/web(Windows)或curl -fsSL https://raw.githubusercontent.com/openchamber/openchamber/main/scripts/install.sh | bash
原 opencode 自带 Web UI(
opencode web)与第三方 pk-opencode-webui 已弃用。4096 端口现仅提供 headless API(opencode serve)。
AI 请求文件操作或命令执行时,微信会收到通知。可以直接在微信中审批,无需打开 Web UI:
/allow(/a) — 批准权限请求(允许一次)/deny(/d) — 拒绝权限请求/trust(/t) — 信任权限(始终允许,不再询问)/plist(/p,/pending) — 查看待审批权限列表
命令后跟可选的编号或 requestID。
不指定参数时默认为 #1(最新请求)。
/plist(/p, /pending)会自动同步服务器状态,清理已过期或已处理的请求。
也可以通过 Web UI 审批:http://localhost:2048 → 进入会话 → 权限弹窗。
wechat-adapter.js 支持环境变量覆盖默认配置:
| 变量 | 默认值 | 说明 |
|---|---|---|
OPENCODE_SERVER |
http://localhost:4096 |
OpenCode 服务器地址 |
OPENCODE_AUTH |
opencode:opencode |
Basic Auth 用户名:密码 |
⚠️ 重要提示:首次使用,必须先向微信机器人发送一条消息(如/help),之后才能收到主动通知。 wechat-acp 驱动的机器人仅在收到微信消息后的窗口内才能推送事件。不先发一条消息,机器人无法主动联系你。
AI 响应以 🤖\n 为前缀发送文本内容。根据当前过滤级别不同,展示内容不同:
- FULL:实时流式输出,包含推理过程、工具调用和文件差异。触发 iLink 限流时自动截断并通知用户,日常使用注意配额消耗
- PAD(默认,推荐):累积文本后一次性发送,等待中显示处理提示,仅发送 AI 文本回复,无丢消息风险
- PHONE:极简模式,仅显示 AI 文本回复和错误
iLink API 对每个 context_token 有约 5 次 sendmessage 配额,超过后回复会被截断。可通过 /quota 命令设置超长回复策略。详见 限流触发条件与防护机制。
在微信中发送命令给机器人,支持以下命令。通知消息中可直接回复答案或审批,无需输入命令前缀。
| 命令 | 别名 | 说明 |
|---|---|---|
/list |
/l /ls /sessions |
查看当前工作区会话列表;/l all [+数字] 查看全部会话前N条 |
/switch <编号|ID> |
/s /sw |
切换会话 |
/new <会话名> |
/nl /create |
在当前工作区新建会话并自动切换 |
/compact |
/summarize |
压缩当前会话,节省上下文 tokens |
| 命令 | 别名 | 说明 |
|---|---|---|
/plan |
/pl |
切换到 plan 模式(规划方案) |
/build |
/bu |
切换到 build 模式(执行代码,默认) |
| 命令 | 别名 | 说明 |
|---|---|---|
/models |
— | 列出所有可用模型(按 provider 分组) |
/switchmodel <provider/model> |
/sm |
切换当前会话模型,下次消息使用;不加参数查看当前模型 |
/rmmodel |
— | 清除模型覆盖,恢复会话默认模型 |
| 命令 | 别名 | 说明 |
|---|---|---|
/undo |
— | 撤销上一条消息及其所有文件改动(需要 Git 仓库) |
/redo |
— | 恢复已撤销的消息 |
控制 AI 响应在微信中的展示粒度:
| 命令 | 别名 | 说明 |
|---|---|---|
| `/level [f | p | ph]` |
/f |
— | FULL 模式:实时流式输出,显示所有信息 |
/pd |
— | PAD 模式:累积文本后一次性发送,仅 AI 文本回复(默认,推荐) |
/ph |
— | PHONE 模式:极简模式,仅显示 AI 文本回复和错误 |
/thinking |
— | 切换思维推理过程显示/隐藏(不影响处理提示) |
| `/quota [t | n | c]` |
/continue |
/g /get /cont |
续发模式:从消息队列取出下一条(显示进度 done/total) |
/clear-continue |
/x /gc |
清除待续发内容 |
⚠️ FULL 模式(/f)注意配额消耗。 每次flushToWeChat()消耗一次 iLinksendmessage调用,每个context_token约 5 次上限。流式刷新上限FULL_QUOTA_LIMIT=4,预留 1 次给结束 flush。超限时自动截断并发送通知,完整内容请在 OpenChamber Web UI (http://localhost:2048) 查看。超长回复策略(
/quota): 影响所有模式。别名:t/trunc,n/notif,c/cont。
truncate(默认)— 静默截断,超限部分直接丢弃notify— 截断并发通知:"回复已截断,超出上限"continue— 保存超限文本到 FIFO 消息队列(每段 ≤4000 字符), 发/g(/get/cont)逐条取出,显示进度(done/total+ 剩余条数), 发/x(/gc)清除待续发内容
AI 暂停等待输入时,微信会收到通知。可直接回复内容,或用以下命令:
| 命令 | 别名 | 说明 |
|---|---|---|
/answer [编号] <内容> |
/ans |
回答指定编号的问题(默认第1题) |
/skip [编号] |
/pass /ps |
跳过指定问题(默认当前) |
/qlist |
/ql /questions |
查看所有待回答问题 |
/qshow |
/qc /qcurrent |
显示当前问题的完整内容 |
/qselect <编号> |
/qs /qsel |
选中指定问题为当前活跃 |
AI 请求文件操作或命令执行时,微信会收到通知。可直接在微信中审批:
| 命令 | 别名 | 说明 |
|---|---|---|
/allow [编号|ID] |
/a |
批准权限请求(默认最新) |
/deny [编号|ID] |
/d |
拒绝权限请求 |
/trust [编号|ID] |
/t |
信任权限(不再询问) |
/plist |
/p /pending |
查看待审批权限列表(自动同步服务器状态) |
| 命令 | 别名 | 说明 |
|---|---|---|
/workspace [add|del] |
/ws |
查看/切换/添加/删除工作区 |
/sd |
— | 从 OpenCode 数据库同步所有项目工作区 |
/status |
/st |
查看任务运行状态 |
/cancel |
/c |
取消当前 AI 执行 |
| 命令 | 别名 | 说明 |
|---|---|---|
/mute |
/m |
开关主动通知(默认开启) |
/notify |
/n |
查看通知设置与订阅状态 |
/autoclean [天数] |
/ac |
查看/设置不活跃订阅自动清理天数 |
/testnotify |
— | 发送测试通知(调试用) |
/help |
/h |
显示帮助信息 |
💡 通知消息中可直接回复答案或权限审批,无需输入命令前缀。 💡 未识别的消息将转发给当前选中的 AI 会话。
机器人通过 SSE 监听 OpenCode 事件,实时推送通知到微信:
| 事件 | 通知格式示例 | 说明 |
|---|---|---|
| 会话完成 | ✅ MyProject · 完成 |
任务执行完毕 |
| 会话出错 | ❌ 错误类型 + 错误详情 |
执行中发生错误 |
| 需要回答 | [Session] 💬 需要你回答 + 问题内容 + 操作提示 |
AI 在等待用户输入,多条自动排队依次处理 |
| 需要权限 | #1 [Session] 操作 + \n路径 + 操作提示 |
AI 请求文件/命令执行权限,多条分别审批 |
| 会话跟随 | 🔄 已跟随到会话「名称」 |
TUI 切换到其他会话时自动跟随 |
| 新会话创建 | 🆕 新会话已创建,已自动跟随 |
当前工作区下新建会话时自动跟随 |
| 疑似卡死 | 🔴 卡死\n「名称」已运行X分钟无响应 |
10 分钟无响应(硬超时预警) |
| 可能卡住 | ⏰ 可能卡住\n「名称」已X分钟无活动 |
5 分钟无活动(软超时提醒) |
| 重试循环 | 🔄 AI重试循环\n已连续重试N次,请检查 |
连续 3 次重试未恢复 |
通知同时发送给所有已订阅的微信用户(非静音状态)。 通知通过 wechat-acp 的
agent_message_chunk累积,批量合并后发送到微信。
权限通知操作提示: #N 行后自动附加 /allow (/a) 批准 | /deny (/d) 拒绝 | /trust (/t) 信任 | +<编号>
问题通知操作提示: 末尾自动附加 /ans (/answer) <内容> 提交、/skip (/pass, /ps)、/qlist (/ql, /questions) 等操作提示
question.asked 事件支持自动排队。如果当前正有未回答的问题,新到达的问题会进入队列,当前问题处理完毕后自动推送下一题。超过 5 分钟未处理的问题自动过期。可通过 /qlist、/qshow、/qselect 管理多题。
在收到问题通知或权限通知时,可以直接在微信中回复(输入答案或审批命令),无需手动输入 /answer 或 /allow 前缀——但需要记住对应命令格式(如通知末尾的提示)。
运行时自动生成,均已加入 .gitignore:
| 文件 | 说明 |
|---|---|
.wechat-session.json |
当前选中的 OpenCode 会话 ID |
.wechat-subscribers.json |
微信用户订阅列表 |
.wechat-workspaces.json |
工作区预设(名称 + 路径) |
.wechat-workspace-current.json |
当前工作区路径 |
.wechat-settings.json |
全局设置(如不活跃清理阈值) |
.wechat-filter.json |
信息过滤级别 + 超长回复策略持久化存储(full/pad/phone + quota) |
.wechat-adapter.log |
运行时日志(超 200MB 自动截断至 100MB) |
.tool-versions.json |
版本锁定文件(setup.bat 读取此文件安装/校验版本) |
基于 Vitest v4 + v8 覆盖率,测试 src/utils.js 的纯函数和 wechat-adapter.js 的集成逻辑。
npm test # 运行全部 164 个测试
npm run test:coverage # 运行并生成覆盖率报告
npm run test:watch # 监听模式测试覆盖:
| 文件 | 类型 | 覆盖功能 | 用例 |
|---|---|---|---|
tests/unit/utils/helpers.test.js |
单元 | levelIcon/levelDesc/levelLabel/quotaModeLabel/summarizeText/resolveFilterLevel/resolveQuotaMode/getAllQuestions/parseWorkspaceArg/parseSessionIndex/formatDuration/normalizeDir/wsPathEqual/makeWsName/formatReply/formatToolInput/splitContinuationMessages |
81 |
tests/unit/utils/edge-cases.test.js |
单元 | 上述函数的 null/undefined/空串/边界值/特殊路径极限输入 | 47 |
tests/unit/utils/question.test.js |
单元 | formatQuestionBody(问答提示拼接) |
11 |
tests/unit/utils/notification.test.js |
单元 | formatReply 对 OpenAI/Claude 输出格式的处理 |
7 |
tests/integration/event-format.test.js |
集成 | eventToNotification 实际调用(8 种 SSE 事件 → 中文通知) |
4 |
当前 164 测试 / 0 失败 / 100% 覆盖率(119 stmts / 107 branches / 26 funcs / 88 lines)。