# 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 `:AES-128-ECB 加密并上传本地文件,再发送给已有上下文的微信用户。 - `wxb send --image `:校验本地图片 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 --text "收到" --json "来自 PowerShell 管道的回复" | node .\src\cli\index.js send --user --stdin --json node .\src\cli\index.js send --user --file "C:\path\to\report.pdf" --json node .\src\cli\index.js send --user --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 "张三" 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 `。 文本、文件和图片发送: ```powershell node .\src\cli\index.js send --user --text "收到" --json node .\src\cli\index.js send --user --file "C:\path\to\report.pdf" --json node .\src\cli\index.js send --user --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 "张三" node .\src\cli\index.js alias get 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 --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` 失败,显式下载时返回附件或下载失败状态。