Files
2026-05-18 20:43:43 +08:00

310 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WeChat-iLink Bridge
[![CI](https://github.com/DONGH4O/wechat-ilink-bridge/actions/workflows/ci.yml/badge.svg)](https://github.com/DONGH4O/wechat-ilink-bridge/actions/workflows/ci.yml)
License: [MIT](LICENSE)
Public npm bin 公开稳定 CLI bin `wxb` 和 P2 MCP adapter bin `wxb-mcp`;`wxb-spike` 不作为公开 bin 发布。
WeChat-iLink Bridge(`wxb`)是一个面向 AI Agent 的微信 iLink 本地桥接 CLI。当前源码版本为 `0.2.0`:`wxb send` 支持文本、文件、图片和可选 typing 状态,`wxb-mcp` 暴露 fetch/send/status/listUsers/analyzeMedia 工具,上传密钥和 CDN URL 继续只在 bridge 内部流转。当前 npm 已发布版本仍为 M12 beta。
## P0 能力范围
- `wxb login`:扫码登录并把账号凭证写入本地状态目录。
- `wxb accounts`:列出账号,stdout 不输出 token。
- `wxb status`:查看账号、本地游标、会话数和消息历史数。
- `wxb fetch`:短超时长轮询一批入站消息,先写入本地历史,再输出给 Agent。
- `wxb send`:向已有入站上下文的用户发送文本,支持 `--text` 和 `--stdin`。
- 默认 `fetch` 仍只识别非文本消息类型和元数据;显式使用 `--download-media` 时下载并保存附件。
## M7 能力范围
- `wxb poll`:前台重复执行 fetch,用于轻量 keepalive 和本地处理。
- `wxb heartbeat`:执行一次计划任务友好的 keepalive fetch,不启动 HTTP 服务。
- `wxb alias set/get/list/resolve/remove`:为 opaque `fromUserId` 维护可读别名。
- `wxb queue list/clear`:查看或清空延迟补发队列。
- `wxb cleanup`:按消息保留天数、附件保留天数和最大历史条数清理本地状态。
- invalid context 触发的发送失败会进入延迟补发队列;后续收到同一用户入站消息时,只补发队列中的第一条。
## M8 能力范围
- `wxb fetch --download-media`:下载图片、文件、语音和视频到本地 `inbox`。
- 支持 AES-128-ECB 解密,兼容 hex key、base64 原始 16 字节 key、base64 hex 字符串 key。
- 图片、语音、视频缺少协议 MIME 时,会根据下载后的文件头推断常见类型和扩展名。
- CLI 输出附件绝对路径、文件名、MIME、字节数和下载状态。
- CLI 不输出 AES key、CDN 下载 URL 或签名查询参数。
- 下载失败不会影响同一条消息中的文本项输出,也不会阻断游标和消息历史写入。
- 附件文件名会清理 Windows 保留字符、路径穿越和极长文件名。
## M11 能力范围
- `wxb send --file <path>`:AES-128-ECB 加密并上传本地文件,再发送给已有上下文的微信用户。
- `wxb send --image <path>`:校验本地图片 MIME,上传并发送图片消息。
- `wxb send --typing`:发送前获取 `typing_ticket` 并显示输入状态,发送后尽力停止输入状态。
- 本地文件不存在、目录路径、超过 `WX_MAX_UPLOAD_BYTES`、未知 MIME 或图片模式传入非图片文件时返回结构化错误。
- stdout 不输出上传 URL、AES key、签名 query、bot token 或 context token。
## M14 MCP Adapter
- `wxb-mcp`:stdio MCP server,复用现有 core library 和本地状态目录。
- MCP tools:`fetchMessages`、`sendText`、`sendFile`、`listUsers`、`status`、`analyzeMedia`。
- Tool schema 不包含 `context_token`;Agent 只传账号、用户、文本或本地文件路径。
- 媒体内容继续通过 `attachments[].path` 或本地 `filePath` 交付,不暴露 CDN URL、上传参数或 AES key。
- 使用说明和客户端配置示例见 `docs/m14-mcp-adapter.md`。
## M15 多模态辅助
- `analyzeMedia`:检查本地媒体路径,返回 MIME、bytes、sha256、图片尺寸和 text-like 文件预览。
- 图片问答、语音转写、视频摘要默认返回 `MULTIMODAL_HELPER_UNAVAILABLE` 降级结果;真实模型能力由 Agent 使用 `attachments[].path` 自行调用,或由自定义 adapter host 注入 optional helper。
- 不需要模型 API key 时,bridge 的 fetch/send/媒体下载/媒体发送仍完整可用。
- helper 失败不会修改 cursor、seen ID、context token 或消息历史。
- 使用说明见 `docs/m15-multimodal-helper.md`。
## 从源码安装
Windows PowerShell:
```powershell
git clone https://github.com/DONGH4O/wechat-ilink-bridge.git
cd wechat-ilink-bridge
npm.cmd test
node .\src\cli\index.js help
```
可选安装为本机全局 `wxb` 命令:
```powershell
npm.cmd install -g .
wxb help
```
## npm Beta 安装
当前公开 beta 已可通过 `@dongh4o/wechat-ilink-bridge@beta` 安装,npm beta dist-tag 仍指向 `0.1.0-beta.1`:
```powershell
npm.cmd install -g @dongh4o/wechat-ilink-bridge@beta
wxb help
wxb status --json
```
发布前本地 `.tgz` smoke:
```powershell
npm.cmd pack
npm.cmd install -g .\dongh4o-wechat-ilink-bridge-0.2.0.tgz
wxb help
wxb status --json
```
## 稳定版准备状态
- M13 稳定版候选准备已完成,GitHub Actions CI 已在 Windows 2025、Ubuntu、macOS 和 Node.js 18/20/22 上通过。
- 最新 M13 CI 验证记录见 `docs/m13-validation-report.md`。
- `0.1.0` stable 发布仍需人工确认后再执行 version bump、`CHANGELOG.md` 稳定版条目、Git tag、GitHub Release 和 npm stable publish。
- 安装、登录、发送、媒体和状态文件的常见问题见 `docs/troubleshooting.md`。
macOS/Linux:
```bash
git clone https://github.com/DONGH4O/wechat-ilink-bridge.git
cd wechat-ilink-bridge
npm test
node ./src/cli/index.js help
```
## Windows PowerShell 快速开始
建议先指定测试状态目录,避免读写真实默认目录:
```powershell
$env:WX_STATE_DIR="C:\tmp\wxb-test"
```
运行测试:
```powershell
npm.cmd test
npm.cmd run pack:dry-run
```
如果 PowerShell 执行策略拦截 `npm.ps1`,使用 `npm.cmd` 即可,不需要修改系统策略。
直接从源码运行 CLI:
```powershell
node .\src\cli\index.js help
node .\src\cli\index.js login --json
node .\src\cli\index.js accounts --json
node .\src\cli\index.js status --json
node .\src\cli\index.js fetch --timeout 1000 --json
node .\src\cli\index.js fetch --timeout 3000 --download-media --json
node .\src\cli\index.js send --user <fromUserId> --text "收到" --json
"来自 PowerShell 管道的回复" | node .\src\cli\index.js send --user <fromUserId> --stdin --json
node .\src\cli\index.js send --user <fromUserId> --file "C:\path\to\report.pdf" --json
node .\src\cli\index.js send --user <fromUserId> --image "C:\path\to\image.jpg" --typing --json
node .\src\cli\index.js poll --limit 3 --interval 1000 --json
node .\src\cli\index.js heartbeat --json
node .\src\cli\index.js alias set <fromUserId> "张三"
node .\src\cli\index.js cleanup --dry-run --json
```
常用全局配置也可以放在命令前:
```powershell
node .\src\cli\index.js --state-dir "C:\tmp\wxb 测试 状态" status --json
node .\src\cli\index.js --base-url "https://ilinkai.weixin.qq.com" fetch --json
```
## 配置
配置优先级为 CLI 参数 > 环境变量 > `.env` > 默认值。
| 环境变量 | CLI 参数 | 说明 |
|---|---|---|
| `WX_STATE_DIR` | `--state-dir` | 状态目录;支持空格和中文路径 |
| `WX_BASE_URL` | `--base-url` | iLink API 地址 |
| `WX_CHANNEL_VERSION` | `--channel-version` | iLink channel version |
| `WX_FETCH_TIMEOUT_MS` | `--fetch-timeout-ms` | fetch/send 请求超时 |
| `WX_POLL_INTERVAL_MS` | `--poll-interval-ms` | poll 循环间隔 |
| `WX_MIN_CHUNK_CHARS` | `--min-chunk-chars` | 文本分片最小长度 |
| `WX_MAX_CHUNK_CHARS` | `--max-chunk-chars` | 文本分片最大长度 |
| `WX_MAX_DELIVERY_MESSAGES` | `--max-delivery-messages` | 单次发送最大分片数 |
| `WX_DELAYED_QUEUE_MAX_ITEMS` | `--delayed-queue-max-items` | 每账号延迟补发队列最大条数 |
| `WX_MESSAGE_RETENTION_DAYS` | `--message-retention-days` | cleanup 消息历史保留天数 |
| `WX_ATTACHMENT_RETENTION_DAYS` | `--attachment-retention-days` | cleanup inbox 附件保留天数 |
| `WX_MAX_HISTORY_MESSAGES` | `--max-history-messages` | cleanup 每账号最多保留消息条数 |
| `WX_MAX_UPLOAD_BYTES` | `--max-upload-bytes` | 单个发送文件最大字节数,默认 25 MiB |
默认状态目录:
| 平台 | 默认状态目录 |
|---|---|
| Windows | `%LOCALAPPDATA%\wxb` |
| macOS | `~/Library/Application Support/wxb` |
| Linux | `${XDG_DATA_HOME:-~/.local/share}/wxb` |
账号 token 和 context token 只保存在本地状态文件中,不会出现在默认 JSON stdout。公开 issue、PR、日志或截图前,请删除 `.env*`、本地状态目录、真实 `m*-*.stdout.json` 捕获、CDN 签名 URL、AES key 和完整 token。
## 发布前检查
准备公开 GitHub 或 npm 前,先运行:
```powershell
npm.cmd test
npm.cmd run pack:dry-run
```
检查 `npm pack --dry-run` 的 file list,确保不包含 `.env`、`你的真实测试状态目录`、`m*-*.stdout.json`、`test/fixtures/raw/live-*`、完整 token、CDN 签名 URL 或 AES key。完整流程见 `docs/release-process.md`。
## Agent 收发循环
1. 运行 `fetch --json` 获取新消息。
2. 把 `data.messages` 视为用户输入。
3. 生成回复文本。
4. 使用消息里的 `fromUserId` 调用 `send --user <fromUserId>`。
文本、文件和图片发送:
```powershell
node .\src\cli\index.js send --user <fromUserId> --text "收到" --json
node .\src\cli\index.js send --user <fromUserId> --file "C:\path\to\report.pdf" --json
node .\src\cli\index.js send --user <fromUserId> --image "C:\path\to\image.jpg" --typing --json
```
`--file` 和 `--image` 只接受本地文件路径。bridge 会负责 AES 加密、上传 URL 获取、CDN 上传和 `sendmessage`,Agent 不需要也不应处理上传密钥、CDN 签名 URL 或 `context_token`。
如果 `send` 返回 `NO_CONTEXT_TOKEN`,说明还没有该用户的可用入站上下文,需要先让用户发来一条消息并运行 `fetch`。如果返回 `SESSION_EXPIRED`,需要重新执行 `login`。
## M7 运维命令
前台 poller:
```powershell
node .\src\cli\index.js poll --limit 10 --interval 1000 --timeout 15000 --json
node .\src\cli\index.js poll --limit 10 --interval 1000 --jsonl
```
计划任务 heartbeat:
```powershell
node .\src\cli\index.js heartbeat --timeout 15000 --max-attempts 1 --json
```
无新消息时,如果客户端 timeout 先于 iLink 服务端长轮询返回,heartbeat 会输出 `ok: true` 和 `status: "idle_timeout"`,这表示本次 keepalive 没拿到新消息,但计划任务不应判为失败。
alias 管理:
```powershell
node .\src\cli\index.js alias set <fromUserId> "张三"
node .\src\cli\index.js alias get <fromUserId>
node .\src\cli\index.js alias resolve "张三"
node .\src\cli\index.js send --alias "张三" --text "你好" --json
```
cleanup:
```powershell
node .\src\cli\index.js cleanup --dry-run --message-retention-days 30 --attachment-retention-days 30 --max-history-messages 10000 --json
node .\src\cli\index.js cleanup --message-retention-days 30 --attachment-retention-days 30 --max-history-messages 10000 --json
```
延迟补发队列:
```powershell
node .\src\cli\index.js queue list --json
node .\src\cli\index.js queue clear --user <fromUserId> --json
```
当 `send` 因 iLink 返回 `INVALID_CONTEXT_TOKEN` 失败且尚未投递任何分片时,消息会入队。下一次 `fetch` 收到同一用户携带新 context token 的入站消息时,只尝试补发该用户队列中的第一条,避免一次性刷屏。
## M8 媒体下载
```powershell
node .\src\cli\index.js fetch --download-media --timeout 15000 --json
```
带媒体的消息会包含:
```json
{
"attachments": [
{
"kind": "image",
"fileId": "media id",
"fileName": "safe local file name",
"path": "C:\\tmp\\wxb-test\\inbox\\bot_id\\2026-05-17\\message-1-image.jpg",
"mimeType": "image/jpeg",
"bytes": 12345,
"encrypted": true,
"decrypted": true
}
],
"mediaDownload": {
"requested": 1,
"succeeded": 1,
"failed": 0
}
}
```
如果下载失败,消息仍会输出文本和元数据,并在对应 item 上标记 `download.ok: false`。
如果 `download.error.code` 为 `MEDIA_URL_MISSING` 且 `metadata` 为空,请保留该 item 的 `diagnostics.itemKeys` 和 `diagnostics.payloadKeys`,用于补齐真实协议字段映射;这些诊断只包含字段名,不包含 token。
## M8 验收清单
- Windows PowerShell 下 `npm.cmd test` 全部通过。
- Windows 下能完成 `login`、`fetch`、`send` 手工链路。
- `poll` 连续运行不会破坏游标。
- `heartbeat` 可被计划任务调用,不启动常驻服务。
- `cleanup --dry-run` 与实际清理数量一致,且不删除账号凭证和 context token。
- alias 不影响 userId 直发。
- 延迟补发每次入站最多补发一条。
- `WX_STATE_DIR` 包含空格或中文时命令能正常读写状态。
- `fetch --download-media` 能保存图片/文件/语音/视频,并返回绝对路径。
- 下载失败不会影响文本项处理。
- 附件路径被规范化到 inbox 内,不允许路径穿越。
- 损坏 JSON/JSONL 状态文件会返回结构化错误和恢复提示。
- stdout/stderr/错误 JSON 不输出完整 `botToken` 或 `contextToken`。
- 默认非文本消息不会导致 `fetch` 失败,显式下载时返回附件或下载失败状态。