给想修 bug、加功能、写频道插件,或者读代码理解架构的人。 用户向使用文档:README.md。
- 项目快照
- 5 分钟环境准备
- 仓库结构
- 构建管线
- 本地运行的三种模式
- 测试
- 架构地图
- 子系统速查表
- acpx / SDK / 插件解析顺序
- 配置与运行时文件
- 代码风格与约定
- 按场景找入口
- Plan / Spec 写作流程
- 提交、PR 与发布
- 拓展阅读
weacpx 是 "消息频道 ↔ 命令路由 ↔ acpx 会话驱动" 的桥接系统:
- 频道:内置
weixin;feishu/yuanbao通过一方插件包分发;任何符合WeacpxPlugin契约的 npm 包都可以加进来。 - 命令路由:解析微信/飞书/元宝/CLI 收到的
/ss、/agent、/group等 slash 命令,普通文本作为 prompt 喂给当前会话。 - Transport:把"会话 ensure/prompt/cancel/setMode"统一为
SessionTransport接口,具体实现两套——acpx-cli(直接 spawnacpx,可选node-pty分配 PTY)和acpx-bridge(独立 bridge 子进程 + JSONL 协议)。 - Orchestration(可选):coordinator 会话下委派多个 worker,跟踪进度、问题、人类确认、分组汇总。可通过
weacpx mcp-stdio暴露给外部 MCP host。 - Daemon:
weacpx start/status/stop,PID + status + log 落在~/.weacpx/runtime/。 - Monorepo:
packages/channel-feishu、packages/channel-yuanbao作为 npm workspaces 与主包同仓发布。
- Node.js ≥ 22 或 Bun(Bun 用于开发脚本与 build;运行时也可用 Node)
- 一个可用的微信 / 飞书 / 元宝账号(看你测哪个频道;只读取代码可以省)
- 本机能跑
acpx和你想接的 agent CLI(codex / claude / gemini …)
git clone https://github.com/gadzan/weacpx
cd weacpx
bun install # 同时装根包和 packages/* 的依赖(workspaces)bun run build # 主包:dist/cli.js + dist/bridge/bridge-main.js + dist/plugin-api.{js,d.ts}
bun run build:packages # 主包 + 全部 packages/*(发布前必须)bun run dev # 等价于 bun run ./src/cli.ts run,前台运行 daemon或者只跑 dry-run(不连 IM):
bun run dry-run --chat-key wx:test -- "/ss new demo --agent codex --ws backend" "/status"npm test # tsc --noEmit + tests/unit/**/*.test.ts跑通这一步之后,欢迎开始动代码。
weacpx/
├── src/ # 主包源码
├── packages/ # 一方频道插件
│ ├── channel-feishu/
│ └── channel-yuanbao/
├── tests/
│ ├── unit/ # 默认单测
│ ├── smoke/ # 需要真实 acpx / 真实账号
│ ├── helpers/ # 共享 fixture / mock
│ └── fixtures/ # 静态数据
├── docs/ # 用户文档 + 本指南 + 设计文档
│ ├── superpowers/ # plans/ + specs/,工作流详见下文
│ └── releases/ # 历史发版说明
├── scripts/ # 测试 runner、发布前校验
├── package.json # 同时是 npm workspaces 根
├── bun.lock
├── tsconfig.json
├── tsconfig.plugin-api.json
├── AGENTS.md # 项目规约(CLAUDE.md 是它的符号链接)
└── README.md
每一个目录一句话说清职责,更深入的内容见对应模块文档。
| 目录 | 职责 | 入口 / 关键文件 |
|---|---|---|
src/cli.ts |
CLI 总入口,weacpx <command> 派发 |
runCli() |
src/main.ts |
buildApp() 装配运行时;resolveRuntimePaths() 路径解析 |
buildApp |
src/run-console.ts |
启动序列:channel → daemon runtime → consumer lock → channel start | runConsole() |
src/console-agent.ts |
把入站消息桥接到 router | ConsoleAgent |
src/channels/ |
频道注册中心;内置 weixin;暴露 MessageChannelRuntime 给插件 |
channels/types.ts、channels/plugin.ts |
src/commands/ |
命令解析 + handler + router | command-router.ts、parse-command.ts |
src/sessions/ |
逻辑会话(state.json 持久化)+ AsyncMutex 串行化 | session-service.ts |
src/transport/ |
acpx 桥接抽象 + cli/bridge 两实现 | transport/types.ts、acpx-cli/、acpx-bridge/ |
src/bridge/ |
acpx-bridge 子进程入口与 JSONL 协议 | bridge-main.ts、bridge-server.ts、bridge-runtime.ts |
src/orchestration/ |
多 agent 编排服务 + IPC server/client + 状态机 | orchestration-service.ts、orchestration-server.ts |
src/mcp/ |
weacpx mcp-stdio 实现,把 orchestration 暴露成 MCP server |
weacpx-mcp-server.ts、weacpx-mcp-tools.ts |
src/daemon/ |
daemon 控制器、status/PID 文件、运行时元数据 | daemon-controller.ts、daemon-runtime.ts |
src/plugins/ |
插件加载、CLI、doctor、包管理器抽象、签名校验 | plugin-loader.ts、plugin-cli.ts、plugin-doctor.ts |
src/plugin-api.ts |
公共插件 API 类型再导出(编译产物 dist/plugin-api.d.ts) |
— |
src/state/ |
state.json 持久化 + DebouncedStateStore(50ms 写合并) |
state-store.ts、debounced-state-store.ts |
src/config/ |
config.json 加载/写入/默认模板 |
config-store.ts、load-config.ts |
src/recovery/ |
缺失可选依赖时的自动恢复(auto-install-optional-dep.ts) |
— |
src/process/ |
跨平台子进程封装 | — |
src/logging/ |
bounded app.log、按级别过滤 |
app-logger.ts |
src/formatting/ |
出站文本/任务渲染 | — |
src/util/ |
writePrivateFileAtomic + proper-lockfile 等通用工具 |
private-file.ts |
src/weixin/ |
内置 weixin 频道 + 媒体管线 + consumer lock | monitor/、messaging/ |
src/weixin-sdk.ts |
weixin SDK 解析器,支持 WEACPX_WEIXIN_SDK 覆盖 |
loadWeixinSdk() |
src/dry-run.ts |
不连 IM 跑 router 的入口 | bun run dry-run |
src/login.ts |
微信扫码登录流程 | weacpx login |
src/doctor/ |
weacpx doctor 诊断套件 |
— |
每个插件包结构一致:
packages/channel-<name>/
├── src/
│ ├── index.ts # 默认导出 WeacpxPlugin
│ ├── channel.ts # implements MessageChannelRuntime
│ ├── <name>-provider.ts# implements ChannelCliProvider
│ └── ...
├── dist/ # bun build 产物 + tsc emit 的 .d.ts
├── package.json # peerDependencies.weacpx (optional)
├── tsconfig.json # 继承根 tsconfig,emitDeclarationOnly
└── README.md
写新频道插件请看 docs/plugin-development.md。
package.json 中关键 script:
| Script | 作用 |
|---|---|
bun run build |
主包:bun build 出 dist/cli.js、dist/bridge/bridge-main.js、dist/plugin-api.js + tsc -p tsconfig.plugin-api.json 出 dist/plugin-api.d.ts |
bun run build:plugin-api |
单独刷新 dist/plugin-api.d.ts(写新公共类型时用) |
bun run build:channel-feishu |
主包 + feishu 插件 |
bun run build:channel-yuanbao |
主包 + yuanbao 插件 |
bun run build:packages |
主包 + 全部 packages/* |
bun run verify:publish |
build:packages + scripts/verify-publish.mjs(发布前必跑) |
bun run dev |
前台跑主包源码(不需要先 build) |
bun run dry-run -- ... |
dry-run 入口 |
bun run login |
微信登录 |
要点:
bun build --target node --external node-pty:node-pty不打进 bundle,运行时由node_modules解析。packages/*同理--external weacpx。- 主包对外只导出
weacpx/plugin-api;其它路径(weacpx/dist/*、weacpx/src/*)不是稳定 API,别在外部依赖。 - 插件包用
tsc -p packages/<name>/tsconfig.json单独 emit.d.ts,因为 bun build 目前不出.d.ts。
最常用。直接跑 src/cli.ts run,热修改,不打 dist。
bun run dev # 前台跑,Ctrl-C 退出
bun run login # 单独完成微信扫码(或在另一个 shell)适合:调试 router、channel、orchestration、transport。
bun run build
node ./dist/cli.js start
node ./dist/cli.js status
node ./dist/cli.js stop适合:复现"用户装好以后跑出来"的状态;测 bin/weacpx 入口;验证打包后 node-pty 解析等。
bun run dry-run --chat-key wx:test -- \
"/agent add codex" \
"/ws new backend -d /absolute/path/to/backend" \
"/ss new demo -a codex --ws backend" \
"/status"复用同一套 buildApp + router + transport,把 IM 入站换成命令行参数,把出站打印到终端。适合复现命令解析、session lifecycle 的 bug,单测无法复现的复杂时序也能在这里手动跑通。
--chat-key 任意值都可以,建议用 wx:test / feishu:test / yuanbao:test 模拟不同频道路由。
完整说明:docs/testing.md。简要:
| 目录 | 跑法 | 何时用 |
|---|---|---|
tests/unit/ |
npm test / npm run test:unit |
默认;镜像 src/ 结构;CI 跑这套 |
tests/smoke/ |
npm run test:smoke |
真实 acpx / 真实 IM 协议;本地手动跑,不进 CI |
tests/integration/ |
(尚未启用) | 跨模块跨进程的协作测试,未来扩 |
tests/helpers/、tests/fixtures/ |
— | 测试公用 |
测试 runner:scripts/run-tests.mjs → scripts/run-tests-lib.mjs::buildTestPlan。它先 tsc --noEmit,再为每个 *.test.ts 单独起 bun test。改 runner 行为请只动这两个文件。
约定:
- 任何写盘的 test 用
mkdtemp隔离,rm -rf自清理。 - 时间相关断言不要用
Bun.sleep(20)当同步屏障。要么显式await someExpectedPromise,要么 poll until 条件满足。原因见我们历史踩过的坑。 - 涉及
state.json写盘的测试用例,记得 buildApp 时传stateSaveDebounceMs: 0(tests/unit/main.test.ts顶部已经有 wrapper)。
完整地图:docs/code-wiki.md。这里只画一张总流图,方便先建心智模型。
+------------------------------------------------------+
| 用户在 IM 平台发消息 / CLI 输入 |
+------------------------------------------------------+
|
v
+-----------------------------------------------------+
| MessageChannelRuntime (weixin / feishu / yuanbao …) |
| - chatKey 构造 |
| - 入站去重、媒体落盘 |
| - 出站配额(OutboundQuota) |
+-----------------------------------------------------+
| agent.handle(chatKey, text)
v
+---------------------+
| ConsoleAgent |
+---------------------+
|
v
+---------------------+
| CommandRouter | ← src/commands/
+---------------------+
| |
slash command | | 普通文本
v v
+------------+ +-------------+
| handlers/ | | SessionService → transport.prompt
+------------+ +-------------+
|
v
+-------------------------------------------+
| SessionTransport (acpx-cli | acpx-bridge) |
+-------------------------------------------+
|
v
acpx 子进程
旁路:
- Orchestration 通过
OrchestrationServer(Unix socket / Named Pipe)把多 agent 编排能力对外暴露。weacpx mcp-stdio是它的 MCP-over-stdio 客户端封装。 - Daemon 把
runConsole+ IPC server + heartbeat 包成后台进程;前台weacpx run跳过 daemon 包装。 - State 持久化 走
DebouncedStateStore→StateStore→writePrivateFileAtomic(proper-lockfile跨进程互斥 +write-file-atomic原子 rename + Windows EBUSY 兜底)。
| 子系统 | 入口 | 文档 |
|---|---|---|
| 命令解析与路由 | src/commands/ |
commands-module.md, commands.md |
| Daemon CLI | src/daemon/、src/cli.ts |
daemon-module.md |
| Acpx-Bridge 协议 | src/bridge/ |
docs/2026-03-25-weacpx-acpx-bridge-design.md |
| Orchestration | src/orchestration/ |
docs/2026-04-13-weacpx-orchestration-design.md |
| 外部 MCP 集成 | src/mcp/ |
external-mcp.md |
| 频道管理 | src/channels/ |
channel-management.md |
| 频道插件 SPI | src/plugin-api.ts、src/plugins/ |
plugin-development.md |
| 配置 | src/config/ |
config-reference.md, config-command.md |
| 测试 | tests/、scripts/run-tests* |
testing.md |
| 发布 | scripts/verify-publish.mjs |
release.md |
src/config/resolve-acpx-command.ts:resolveAcpxCommand:
transport.command(config 显式覆盖)- bundled acpx:从主包
node_modules/acpx/...解析(默认dependencies已经声明acpx@^0.6.1) - Shell
PATH
src/weixin-sdk.ts:loadWeixinSdk:
WEACPX_WEIXIN_SDK环境变量- 已安装包
weixin-agent-sdk
src/plugins/plugin-home.ts:resolvePluginHome:
WEACPX_PLUGIN_HOME环境变量- 默认
~/.weacpx/plugins/(独立package.json,与全局 / 项目node_modules隔离)
包管理器自动探测:能跑 bun --version 就用 bun add/remove,否则回退 npm install/uninstall(src/plugins/package-manager.ts)。
默认全在 ~/.weacpx/:
| 路径 | 内容 | 写入方 |
|---|---|---|
~/.weacpx/config.json |
agents、workspaces、channels、plugins、transport 等静态配置 | ConfigStore,CLI |
~/.weacpx/state.json |
sessions、chat_contexts、orchestration 状态 | DebouncedStateStore(50ms 合并)→ StateStore |
~/.weacpx/runtime/daemon.pid |
当前 daemon PID | DaemonRuntime |
~/.weacpx/runtime/status.json |
daemon heartbeat / start_at / log paths | 同上 |
~/.weacpx/runtime/app.log |
bounded 应用日志(轮转) | AppLogger |
~/.weacpx/runtime/orchestration.sock |
Unix socket / \\.\pipe\weacpx-orchestration-<hash> |
OrchestrationServer |
~/.weacpx/plugins/ |
插件 npm home(独立 package.json + node_modules) |
weacpx plugin add/update |
字段细节:docs/config-reference.md。
强约束写在 AGENTS.md(CLAUDE.md 是它的符号链接)。重点:
- 第一性原理:从原始需求出发,不复制模板;目标不清楚先停下来对齐。
- TypeScript 严格模式:
strict: true;不放过any;类型即文档。 - 不写无用注释:除非要解释 WHY(隐性约束、过去事故、特意规避的写法)。删掉解释 WHAT 的注释。
- 不要为不可能发生的场景加
try/catch/ fallback:内部边界相信类型;只在系统边界(用户输入、外部 API)做校验。 - 测试先行:bug 修复要附 failing test → 修复 → test 转绿。修代码不写测试视为未完成。
- 频道:内置只有
weixin;非 weixin 频道必须作为插件包,在src/channels/写 product-specific 通道运行时一律不接受。 - 避免破坏性变更:
weacpx/plugin-api是公开类型;改它要慎重,必要时升WEACPX_PLUGIN_API_VERSION。
新人最容易卡在"我要加 X,从哪里下手"。这张表覆盖最常见的场景:
| 想做的事 | 看这里 / 改这里 |
|---|---|
| 加一个新的 slash 命令 | src/commands/parse-command.ts 加 token;src/commands/handlers/ 加 handler;src/commands/command-router.ts 注册;测试镜像到 tests/unit/commands/ |
| 改某个命令的回复格式 | src/formatting/ 的 render 函数;命令在 router 里调对应 render |
| 加一个新的频道(飞书/Slack/Discord …) | 不要改 src/channels/,看 docs/plugin-development.md,在 packages/channel-<type>/ 起新包 |
| 改 acpx 调用方式(命令行参数、PTY、超时) | src/transport/acpx-cli/ 或 src/transport/acpx-bridge/,保持 SessionTransport 接口稳定 |
| 加 / 改一项 orchestration 能力 | src/orchestration/orchestration-service.ts + orchestration-ipc.ts + orchestration-server.ts;测试在 tests/unit/orchestration/ |
| 改 daemon 启停行为 | src/daemon/;status 字段改了同步更 daemon-status.ts 与文档 |
改 weacpx doctor |
src/doctor/index.ts 与各 probe |
改 weacpx mcp-stdio 暴露的工具 |
src/mcp/weacpx-mcp-tools.ts |
改 state.json schema |
src/state/types.ts + state-store.ts 的解析;考虑迁移 |
| 加可恢复的运行时错误 | src/recovery/;router 里 wire 进对应命令 |
| 加 / 改全局公共类型 | src/plugin-api.ts 再导出 + bun run build:plugin-api |
| 修测试 runner | scripts/run-tests-lib.mjs(buildTestPlan) |
| 加发布前 preflight | scripts/verify-publish.mjs |
docs/superpowers/specs/ 与 docs/superpowers/plans/ 是非强制但强推荐的工作流:
- spec(设计文档):解释"我要解决什么 / 为什么这样做 / 备选方案"。
在动复杂代码前先写一份,命名
YYYY-MM-DD-<topic>-design.md。 - plan(实施计划):把 spec 拆成可被 agent / 自己一步步执行的步骤,命名
YYYY-MM-DD-<topic>.md。
这两份在历史 PR 里通常作为同一次合并的一部分,便于后续考古。
- 提交信息走 conventional commits 风格(
fix:、feat:、docs:、chore:、test:)。 - 一个提交聚焦一件事;diff 越小越容易审。
- 测试与代码改动尽量在同一个 commit 里。
- PR 标题简洁(< 70 字),描述里写:
- 为什么改(链接 issue / spec)
- 怎么改
- 怎么验(手动 / 自动测试)
- 触发 daemon 行为变化的改动(频道、transport、orchestration、state)建议附 dry-run 脚本作证据。
完整流程:release.md。一句话版:
bun run verify:publish # build:packages + scripts/verify-publish.mjs
bun run publish:weacpx
bun run publish:plugins # 升一方插件包时发版时记得:
- 升
package.jsonversion;如果改了一方插件,也升对应packages/*/package.json。 - 在
docs/releases/加发版说明。 - 给 git tag。
- 用户视角:README.md
- 完整命令参考:commands.md
- 频道管理:channel-management.md
- 插件开发:plugin-development.md
- 配置字段:config-reference.md
- 代码地图:code-wiki.md
- 测试约定:testing.md
- 发版流程:release.md
- 多 Agent 编排原理:
2026-04-13-weacpx-orchestration-design.md - Acpx-Bridge 协议:
2026-03-25-weacpx-acpx-bridge-design.md - 项目规约(
AGENTS.md/CLAUDE.md):../AGENTS.md