diff --git a/.gitignore b/.gitignore index 54616ae..10e47b1 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ checksums.txt # Coverage coverage.out coverage.html + +# Local e2e validation reports (generated by test/e2e.sh) +/test/e2e-report*.md diff --git a/README.md b/README.md index 1137390..7c78f74 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ -# Mp2rss-cli(微信公众号订阅,公众号转RSS、JSON) +# Mp2rss-cli(微信公众号 + X / Twitter 订阅管理,统一转 RSS / JSON) -[Mp2RSS](https://mp2rss.bugcode.dev) 的命令行客户端 —— 管理订阅、订阅微信公众号,文章转RSS、JSON,查看历史文章。AI Agent 友好(Claude Code / Cursor skills)。Keywords: wechat, weixin, 微信, 公众号, rss, feed, subscription, 订阅. +> 🚀 **一行命令,把你关注的微信公众号和 X 账号变成 RSS,喂给你最熟悉的阅读器。** + +[Mp2RSS](https://mp2rss.bugcode.dev) 的官方命令行客户端 —— 在终端订阅 / 查询 微信公众号与 X 账号、列出已订阅源、拉历史文章 / 推文流 / 长文流,输出 table 或 JSON。AI Agent 友好(Claude Code / Cursor skills),可作为 Web 控制台的脚本化替代。 + +> **关于 Mp2RSS** —— 把信任的信息源搬进熟悉的阅读器。公众号文章、X 推文 / 长文 → 标准 RSS 2.0 / Atom 1.0 / JSON Feed 1.1 / OPML 2.0,可同步到 Reeder、NetNewsWire、FreshRSS、Miniflux 等阅读器。 + +Keywords: wechat, weixin, 微信, 公众号, twitter, x, rss, atom, jsonfeed, opml, feed, subscription, 订阅, mp2rss. [![Release](https://img.shields.io/github/v/release/areyoubugcoder/mp2rss-cli?display_name=tag&sort=semver)](https://github.com/areyoubugcoder/mp2rss-cli/releases) [![Downloads](https://img.shields.io/github/downloads/areyoubugcoder/mp2rss-cli/total)](https://github.com/areyoubugcoder/mp2rss-cli/releases) @@ -15,11 +21,11 @@ # 一键安装脚本(macOS / Linux) curl -fsSL https://raw.githubusercontent.com/areyoubugcoder/mp2rss-cli/main/scripts/install.sh | sh -# npm(Node ≥ 18) +# npm(Node ≥ 18,跨平台含 Windows) pnpm add -g @mp2rss/cli ``` -也可在 [Releases](https://github.com/areyoubugcoder/mp2rss-cli/releases/latest) 直接下载对应平台二进制。完整安装/卸载说明见 [文档站 · 安装](https://areyoubugcoder.github.io/Mp2RSS/cli/install)。 +也可在 [Releases](https://github.com/areyoubugcoder/mp2rss-cli/releases/latest) 直接下载对应平台二进制,或 `go install` 从源码构建。完整安装/卸载说明见 [文档站 · 安装](https://areyoubugcoder.github.io/Mp2RSS/cli/install)。 升级到最新版本: @@ -28,89 +34,61 @@ mp2rss update # 检查并升级 mp2rss update --check # 只检查不升级 ``` -## CLI 如何使用 +## 使用 -### 1. 安装 CLI - -参考上面 [安装](#安装) 章节。 - -### 2. 登录 +### 登录 ```bash -mp2rss auth login +mp2rss auth login # 浏览器 loopback 授权(推荐) +mp2rss auth login -k # 直传 Feed Key(CI / 无头) +mp2rss auth login --no-browser # SSH 远程:仅打印授权 URL ``` -默认浏览器 loopback 授权,登录后凭证写入 `~/.mp2rss/config.json`。也可用 `mp2rss auth login -k ` 直传 Feed Key(适合 CI / 无头环境),或 `--no-browser` 仅打印授权 URL。查看登录态:`mp2rss auth status`。 - -### 3. 使用 +### 微信公众号 ```bash -mp2rss mp subscribe https://mp.weixin.qq.com/s/xxxxxxxxxx # 订阅一个公众号 -mp2rss mp list # 列出订阅 +mp2rss mp subscribe https://mp.weixin.qq.com/s/xxxxxxxxxx # 参数是任意一篇文章的 URL +mp2rss mp list # 已订阅公众号 mp2rss mp list -q 财经 # 模糊搜索已订阅源 -mp2rss mp articles # 看该公众号历史文章 -mp2rss mp list -o json | jq '.items[].mpName' # 结构化数据 + jq 处理 +mp2rss mp articles # 该公众号历史文章 +mp2rss mp remove # 取消订阅 ``` -> ⚠️ 订阅时传入的是 **公众号文章的 URL**(`https://mp.weixin.qq.com/s/...`),不是公众号名字。 - -## 完整命令参考 - -### 认证 - -| 命令 | 说明 | -|------|------| -| `mp2rss auth login` | 浏览器 loopback 登录;`-k ` 直传;`--no-browser` 仅打印授权 URL | -| `mp2rss auth status` | 查看登录状态、Feed Key 来源(env / config / none)、API URL、最近登录时间 | -| `mp2rss auth logout` | 清空本地 Feed Key(保留 API URL 配置) | - -### 公众号 +> ⚠️ 订阅公众号时传入的是 **文章 URL**(`https://mp.weixin.qq.com/s/...`),不是公众号名字。 -| 命令 | 说明 | -|------|------| -| `mp2rss mp list` | 列出订阅;`-q ` 模糊搜索,`-p ` 翻页,`--page-size ` 每页条数(最大 50) | -| `mp2rss mp search ` | `mp list -q` 的语法糖,flag 与输出一致 | -| `mp2rss mp subscribe ` | 订阅公众号;参数必须是 `mp.weixin.qq.com/s/...` 文章 URL | -| `mp2rss mp remove ` | 按 mpId 取消订阅;`-y` 跳过确认 | -| `mp2rss mp articles ` | 列出指定公众号的历史文章;`-p` / `--page-size`(最大 100) | +### X(Twitter) -### 命令表格说明 - -- 所有命令默认 `-o table`,加 `-o json` 输出结构化数据(`auth login` 例外,仅文本反馈) -- JSON 错误形态统一为 `{"error":{"message":"...","code":}}`,`code` 为 HTTP 状态码或 CLI exit code -- Exit codes:`0` 成功 / `1` 通用错误(网络)/ `2` 参数错误 / `3` 鉴权失败 / `4` 资源不存在 / `5` 上游不可用 - -## 全局参数 +```bash +mp2rss x list # 已订阅 X 账号 +mp2rss x posts # 推文流 +mp2rss x articles # 长文流 +``` -所有子命令都支持以下持久化 flag: +> X 的搜索 / 订阅 / 取消订阅请在 [Web 控制台](https://mp2rss.bugcode.dev/) 完成,CLI 仅暴露读类操作。 -| Flag | 等价环境变量 | 说明 | -|------|-------------|------| -| `-o, --output ` | — | 输出格式,默认 `table` | -| `--api-key ` | `MP2RSS_FEED_KEY` | 覆盖 Feed Key | -| `--api-url ` | `MP2RSS_API_URL` | 覆盖 API 地址(默认 `https://mp2rss.bugcode.dev`) | +### 脚本化 -优先级(高 → 低):命令行 flag> 环境变量> 配置文件> 默认值。 +```bash +mp2rss mp list -o json | jq '.items[].mpName' # 所有命令支持 -o json +``` -## 配置 +📖 **完整命令参考**(全部 flag、JSON schema、错误码、配置文件、全局参数)→ **[在线文档 · CLI](https://areyoubugcoder.github.io/Mp2RSS/cli/)** -本地配置 `~/.mp2rss/config.json`: +快捷入口: +[安装](https://areyoubugcoder.github.io/Mp2RSS/cli/install) +· [登录](https://areyoubugcoder.github.io/Mp2RSS/cli/login) +· [命令总览](https://areyoubugcoder.github.io/Mp2RSS/cli/commands) +· [FAQ](https://areyoubugcoder.github.io/Mp2RSS/cli/faq) -```json -{ - "feed_key": "9f3a2c...(64 位 hex)", - "api_url": "https://mp2rss.bugcode.dev", - "last_login_at": 1747194198, - "last_verify_at": 1747194198, - ... -} -``` +## AI Agent 如何使用 -Feed Key 可在 登录后查看或重置。FAQ / 故障排查见 [文档站 · FAQ](https://areyoubugcoder.github.io/Mp2RSS/cli/faq)。 +Mp2rss 在 `skills/` 目录提供 agent skills,让 AI Agent(Claude Code / Cursor)用自然语言驱动 CLI: -## AI Agent 如何使用 +- [`mp2rss-auth`](skills/mp2rss-auth/SKILL.md) —— 登录态管理(login / logout / status) +- [`mp2rss-mp`](skills/mp2rss-mp/SKILL.md) —— 公众号订阅与文章查询 +- [`mp2rss-x`](skills/mp2rss-x/SKILL.md) —— X 账号已订阅列表、推文流与长文流(只读 3 件套;订阅 / 取消订阅 / 搜索请到 Web 控制台) -Mp2rss 在 `skills/` 目录提供两个 agent skill —— [`mp2rss-auth`](skills/mp2rss-auth/SKILL.md)(登录态管理)和 [`mp2rss-mp`](skills/mp2rss-mp/SKILL.md)(订阅与文章),让 AI Agent 用自然语言驱动 Mp2rss CLI。完整安装与使用步骤见 **[AI Agent 安装指南](docs/agent-install.md)**。 +完整安装与使用步骤见 **[AI Agent 安装指南](docs/agent-install.md)**。 ### 快速安装 @@ -119,7 +97,7 @@ Mp2rss 在 `skills/` 目录提供两个 agent skill —— [`mp2rss-auth`](skill pnpm add -g @mp2rss/cli # 2. 装 Skills,任选其一 -npx skills add areyoubugcoder/mp2rss-cli -y -g # Claude Code / Cursor 通用,一次装齐两个 skill +npx skills add areyoubugcoder/mp2rss-cli -y -g # Claude Code / Cursor 通用,一次装齐三个 skill /plugin marketplace add areyoubugcoder/mp2rss-cli # Claude Code 内置 plugin marketplace /plugin install mp2rss-cli@mp2rss @@ -127,15 +105,13 @@ npx skills add areyoubugcoder/mp2rss-cli -y -g # Claude Code / Cursor mp2rss auth login ``` -### 使用举例 - -安装后在 AI 客户端里直接说自然语言,agent 会自动调用对应 skill: +### 使用举例(自然语言驱动) - 「登录公众号 RSS 服务」/「我的 Feed Key 是什么」→ `mp2rss-auth` - 「订阅这个公众号 https://mp.weixin.qq.com/s/...」→ `mp2rss mp subscribe` - 「我订阅了哪些公众号」/「搜一下我订阅的财经类公众号」→ `mp2rss mp list / search` -- 「看一下 X 这个号的最新文章」→ `mp2rss mp articles` -- 「取消订阅公众号 X」/「把 X 从订阅里删了」→ `mp2rss mp remove` +- 「<公众号名> 最近发了什么」→ `mp2rss mp articles` +- 「取消订阅 <公众号名>」→ `mp2rss mp remove` 所有命令支持 `-o json`,Agent 可直接解析结构化输出做后续处理。 diff --git a/skills/mp2rss-auth/SKILL.md b/skills/mp2rss-auth/SKILL.md index 4c26133..7261c04 100644 --- a/skills/mp2rss-auth/SKILL.md +++ b/skills/mp2rss-auth/SKILL.md @@ -33,7 +33,7 @@ mp2rss auth login [-k ] [--no-browser] | Mode | Command | Description | |------|---------|-------------| | 浏览器(默认) | `mp2rss auth login` | 打开浏览器走 loopback 授权,登录后自动写入 `~/.mp2rss/config.json` | -| Feed Key 直传 | `mp2rss auth login -k ` | 直接传入 Feed Key,跳过浏览器,常用于 CI / 无头环境 | +| Feed Key 直传 | `mp2rss auth login -k ` | 直接传入 Feed Key,**先调用上游 `VerifyAuth` 校验,校验通过后写入 `~/.mp2rss/config.json`**,跳过浏览器流程;适用于 CI / 无头环境 | | 远程模式 | `mp2rss auth login --no-browser` | 不打开浏览器,仅打印授权 URL,复制到本地浏览器打开后手动粘贴 Feed Key | ```bash @@ -68,11 +68,15 @@ JSON shape(已登录): "source": "config", "apiUrl": "https://mp2rss.bugcode.dev", "feedKeyMasked": "abcdef***", + "name": "张三", + "email": "user@example.com", "lastLoginAt": 1705000000000, "lastVerifyAt": 1705000001000 } ``` +`name` / `email` 仅在浏览器登录回调成功时落盘;`-k` / `--no-browser` 流程拿不到这两个字段,会以 `omitempty` 省略。 + JSON shape(未登录): ```json { @@ -100,7 +104,7 @@ mp2rss auth logout - 调用 `mp2rss mp` 任何子命令前如不确定登录状态,应先 `mp2rss auth status -o json` 解析 `loggedIn` 字段 - 时间字段均为 unix 毫秒数(number),不是格式化字符串 -- 字段命名统一 camelCase(`loggedIn` / `apiUrl` / `feedKeyMasked` / `lastLoginAt` / `lastVerifyAt`) +- 字段命名统一 camelCase(`loggedIn` / `apiUrl` / `feedKeyMasked` / `name` / `email` / `lastLoginAt` / `lastVerifyAt`) - 本地配置:`~/.mp2rss/config.json`,目录 `0700` / 文件 `0600` - Feed Key 优先级(高 → 低):命令行 `--api-key`> `MP2RSS_FEED_KEY` 环境变量> 配置文件 - API URL 优先级(高 → 低):`--api-url`> `MP2RSS_API_URL`> 配置文件> 默认 `https://mp2rss.bugcode.dev` diff --git a/skills/mp2rss-mp/SKILL.md b/skills/mp2rss-mp/SKILL.md index 9542e43..304ff62 100644 --- a/skills/mp2rss-mp/SKILL.md +++ b/skills/mp2rss-mp/SKILL.md @@ -1,7 +1,7 @@ --- name: mp2rss-mp version: 0.1.0 -description: 微信公众号订阅与文章管理(基于把公众号转成 RSS 的 Mp2rss 服务)—— 订阅 / 列出 / 取消订阅微信公众号,查询某个公众号的历史文章,按关键词模糊搜索已订阅源。匹配「订阅这个公众号 <文章 URL>」「把这个公众号转成 RSS」「订阅 」「我订阅了哪些公众号」「列一下我的公众号 RSS」「这个公众号最近发了什么」「 的历史文章」「拉一下 X 这个号的文章」「取消订阅 X」「把 X 从订阅里删了」「搜一下我订阅的公众号 X」「mp2rss mp list / search / subscribe / remove / articles」。订阅时传入的是公众号「任意一篇文章的 URL」(mp.weixin.qq.com/s/...),不是公众号名字本身。 +description: 微信公众号订阅与文章管理(基于把公众号转成 RSS 的 Mp2rss 服务)—— 订阅 / 列出 / 取消订阅微信公众号,查询某个公众号的历史文章,按关键词模糊搜索已订阅源。匹配「订阅这个公众号 <文章 URL>」「把这个公众号转成 RSS」「订阅 」「我订阅了哪些公众号」「列一下我的公众号 RSS」「这个公众号最近发了什么」「<某公众号> 的历史文章」「拉一下 <某公众号> 这个号的文章」「取消订阅 <某公众号>」「把 <某公众号> 从订阅里删了」「搜一下我订阅的公众号 <某关键词>」「mp2rss mp list / search / subscribe / remove / articles」。订阅时传入的是公众号「任意一篇文章的 URL」(mp.weixin.qq.com/s/...),不是公众号名字本身。⚠️ 只处理「微信公众号」语义;用户说「X 账号 / 推特 / xUserId / 长文 articles / 推文 posts」之类应当路由到 mp2rss-x,不要进来。 --- # mp2rss-mp Skill @@ -40,6 +40,7 @@ JSON shape: { "items": [ { + "sourceType": "mp", "mpId": 123456, "mpName": "某公众号", "mpAvatarUrl": "https://...", @@ -171,5 +172,5 @@ JSON shape: - **订阅参数最常犯的错**:用户说「订阅这个公众号 」时,`` 必须是 `mp.weixin.qq.com/s/...` 文章链接。识别不到合法文章 URL 时应**先反问用户索要任意一篇文章链接**,而不是直接尝试 - 取消订阅前应先 `mp2rss mp list -q ` 确认 mpId,避免误删 - 文章列表无分页字段,依赖 `--page-size` 控制单次返回上限(最大 100);分页继续查下一页用 `-p` -- 错误 JSON 形态:`{"error":{"message":"...","code":}}` +- 错误 JSON 形态:`{"error":{"message":"...","code":}}`;`code` 优先用 HTTP 状态码(来自上游),否则回退到 CLI exit code(参数 / 鉴权类错误) - Exit codes:`0` 成功;`1` 通用错误(网络);`2` 参数错误;`3` 鉴权失败 → 引导用户跑 `mp2rss auth login`(参考 `mp2rss-auth` skill);`4` 资源不存在(mpId 错或文章 URL 已失效);`5` 上游不可用 diff --git a/skills/mp2rss-x/SKILL.md b/skills/mp2rss-x/SKILL.md new file mode 100644 index 0000000..f2d0be3 --- /dev/null +++ b/skills/mp2rss-x/SKILL.md @@ -0,0 +1,181 @@ +--- +name: mp2rss-x +version: 0.1.0 +description: X(Twitter)账号订阅查看与内容拉取(基于把 X 账号转成 RSS 的 Mp2rss 服务)—— 列出已订阅 X 账号、按 xUserId 分页拉推文流(posts)与长文流(articles)。匹配「我订阅了哪些 X 账号」「列一下我的 X 订阅」「这个 X 账号最近发了什么」「拉一下 的推文」「 的长文」「X 账号 的 articles」「mp2rss x list / posts / articles」。⚠️ CLI 只暴露读类 3 件套;X 账号的「搜索 / 订阅 / 取消订阅」仅在 Web 控制台()操作,CLI 与 Open API 都没有这些写类端点 —— 用户提这类需求时应引导到 Web 控制台,而不是去找 `x subscribe / x remove / x search`(不存在)。 +--- + +# mp2rss-x Skill + +通过 Mp2rss CLI 管理 X(Twitter)信息源的只读访问 —— 列出已订阅 X 账号、按 xUserId 拉推文与长文。 + +## Prerequisites + +- `mp2rss` 二进制已安装(参考 `mp2rss-auth` 的 Prerequisites) +- 已登录:`mp2rss auth status` 显示 `loggedIn: true`(首次使用先跑 `mp2rss auth login`) +- 要操作的 X 账号已在 Web 控制台订阅(CLI 不支持 `subscribe`) + +## 范围边界(重要) + +| 操作 | CLI 是否支持 | 去哪里做 | +|------|--------------|----------| +| `x list` —— 列已订阅 X 账号 | ✅ | 本 skill | +| `x posts` —— 拉推文流 | ✅ | 本 skill | +| `x articles` —— 拉长文流 | ✅ | 本 skill | +| `x subscribe` —— 订阅新 X 账号 | ❌ | Web 控制台 | +| `x remove` —— 取消订阅 X 账号 | ❌ | Web 控制台 | +| `x search` —— 搜索 X 账号 | ❌ | Web 控制台 | + +> 用户说「订阅这个 X 账号」/「把 X 上的 xxx 加进来」/「搜一下 X 上的 xxx」时,**不要尝试构造 `mp2rss x subscribe` 之类的命令**(不存在),直接告诉用户去 Web 控制台操作。 + +## Commands + +### List subscribed X accounts + +``` +mp2rss x list [-q ] [-p ] [--page-size ] [-o json] +``` + +| Flag | Default | Description | +|------|---------|-------------| +| `-q, --query` | — | 按 displayName / username 模糊搜索(仅 server 支持时生效;不支持时退化为客户端筛 X 项) | +| `-p, --page` | 1 | 页码 | +| `--page-size` | 20 | 每页条数(最大 50) | +| `-o, --output` | table | `table` / `json` | + +```bash +mp2rss x list +mp2rss x list -q claude +mp2rss x list -p 2 --page-size 50 +mp2rss x list -o json | jq '.items[].xDisplayName' +``` + +底层调 `GET /open-api/subscriptions?sourceType=x`;若 server 不识别 `sourceType`,CLI 客户端会兜底剔除 mp 项。 + +JSON shape: +```json +{ + "items": [ + { + "sourceType": "x", + "xUserId": "44196397", + "xUsername": "elonmusk", + "xDisplayName": "Elon Musk", + "xAvatarUrl": "https://...", + "xVerified": true, + "xLastItemAt": 1705000050000, + "createdAt": 1705000000000 + } + ], + "total": 12, + "page": 1, + "pageSize": 20 +} +``` + +--- + +### List posts (推文流) + +``` +mp2rss x posts [-p ] [--page-size ] [-o json] +``` + +按 `postedAt DESC` 分页拉某个已订阅 X 账号的推文。 + +| Flag | Default | Description | +|------|---------|-------------| +| `-p, --page` | 1 | 页码 | +| `--page-size` | 20 | 每页条数(最大 50) | +| `-o, --output` | table | `table` / `json` | + +```bash +mp2rss x posts 44196397 +mp2rss x posts 44196397 -p 2 --page-size 50 +mp2rss x posts 44196397 -o json | jq '.items[].content' +``` + +JSON shape: +```json +{ + "items": [ + { + "postId": "1234567890", + "content": "推文正文", + "media": [{"url": "https://...", "type": "image"}], + "retweetedPost": null, + "quotedPost": null, + "threadPosts": [], + "postedAt": 1705000000000 + } + ], + "total": 87, + "page": 1, + "pageSize": 20 +} +``` + +字段说明: +- `media` —— 推文附带的图片 / 视频,`type` 取值如 `image` / `video` +- `retweetedPost` / `quotedPost` —— 原始推文的嵌套对象(可能为 `null`) +- `threadPosts` —— 同一作者的线程后续推文(可能为空数组) +- 未订阅的 `xUserId` 返回 404 `X account is not subscribed`(exit code `4`) + +--- + +### List articles (长文流) + +``` +mp2rss x articles [-p ] [--page-size ] [-o json] +``` + +按 `publishedAt DESC` 分页拉某个已订阅 X 账号的长文(X Articles)。 + +| Flag | Default | Description | +|------|---------|-------------| +| `-p, --page` | 1 | 页码 | +| `--page-size` | 20 | 每页条数(最大 50) | +| `-o, --output` | table | `table` / `json` | + +```bash +mp2rss x articles 44196397 +mp2rss x articles 44196397 -p 2 --page-size 50 +mp2rss x articles 44196397 -o json | jq '.items[].title' +``` + +JSON shape: +```json +{ + "items": [ + { + "url": "https://x.com/.../article/...", + "title": "长文标题", + "description": "长文摘要", + "contentMarkdown": "# Markdown 正文(可能为 null)", + "coverUrl": "https://...", + "publishedAt": 1705000000000 + } + ], + "total": 3, + "page": 1, + "pageSize": 20 +} +``` + +- 未订阅的 `xUserId` 返回 404 `X account is not subscribed`(exit code `4`) +- `contentMarkdown` 是上游推送的 markdown 原文,可能为 `null` + +--- + +## Agent Usage Notes + +- **`xUserId` 是字符串**(X 平台原生数字 ID,序列化为 string),不要按 number 解析;`mp2rss mp` 那边的 `mpId` 才是 int64 +- 解析批量输出统一加 `-o json` +- 时间字段均为 unix 毫秒数(number):`createdAt` / `xLastItemAt` / `postedAt` / `publishedAt` +- 字段命名统一 camelCase:`xUserId` / `xUsername` / `xDisplayName` / `xAvatarUrl` / `xVerified` / `xLastItemAt` / `sourceType` / `postId` / `coverUrl` / `contentMarkdown` +- **不存在的子命令**:`x subscribe` / `x remove` / `x search` —— 用户提相关需求时直接引导去 Web 控制台 ,不要去 CLI 里找替代命令 +- `x posts` / `x articles` **都带分页元数据**(`total` / `page` / `pageSize`)—— 这一点与 `mp articles`(无分页字段)不同,按 `total` 判断是否还有下一页即可 +- `--page-size` 三个命令一致上限 50;超过会返回 `参数错误`(exit code `2`) +- `x list -q ` 的 `-q` 走服务端模糊搜索 displayName / username;若服务端不识别,CLI 会退化为按 `sourceType=x` 客户端过滤(即 `-q` 失效,但不会报错) +- 拿到 `xUserId` 的常规链路:先跑 `mp2rss x list -o json` 从 `items[].xUserId` 取,再喂给 `x posts` / `x articles` +- 错误 JSON 形态:`{"error":{"message":"...","code":}}`;`code` 优先用 HTTP 状态码(来自上游),否则回退到 CLI exit code(参数 / 鉴权类错误) +- Exit codes:`0` 成功;`1` 通用错误(网络);`2` 参数错误(含 `--page-size` 越界);`3` 鉴权失败 → 引导用户跑 `mp2rss auth login`(参考 `mp2rss-auth` skill);`4` 资源不存在(`xUserId` 未订阅或不存在);`5` 上游不可用 diff --git a/test/README.md b/test/README.md index 8a82da5..481bab5 100644 --- a/test/README.md +++ b/test/README.md @@ -8,7 +8,7 @@ |---|---| | `e2e.sh` | bash 端到端脚本,覆盖 plan「端到端验证(阶段一聚焦)」6 大检查项;运行后生成 `e2e-report.md`。 | | `mock-api/main.go` | 最小 Go httptest server,模拟 Mp2rss Open API(4 个 endpoint,内存存储)。仅在未提供真实 `MP2RSS_TEST_FEED_KEY` 时由 `e2e.sh` 自动启动作为替身。 | -| `e2e-report.md` | `e2e.sh` 每次运行后覆盖写入的报告。**生成物,不要手改。** | +| `e2e-report*.md` | `e2e.sh` 运行后在本目录生成的报告(含 stage2),已 gitignore,不进仓库。**生成物,不要手改。** | ## 重要:mock-api 不是产线依赖 @@ -34,11 +34,4 @@ export MP2RSS_TEST_ARTICLE_URL=https://mp.weixin.qq.com/s/<一篇真实文章> ./test/e2e.sh ``` -环境变量优先级与默认值见 `e2e.sh` 顶部注释。 - -## 当前状态 - -骨架阶段(Tasks #2/#3 完成前): - -- `e2e.sh` 已能完整跑通,基础设施(环境变量解析、临时 HOME 沙箱、mock 自启与回收、报告生成)就绪。 -- 6 个 step 中只有 step 1(git identity)做实校验,其余 5 个均输出 `SKIP`,待 CLI 命令、Makefile、release-please 配置就绪后逐个把 step 函数内的 `TODO` 替换为真实调用。 +环境变量优先级与默认值见 `e2e.sh` 顶部注释,当前覆盖范围以 `e2e.sh` 内 step 注释为准。 diff --git a/test/e2e-report-stage2.md b/test/e2e-report-stage2.md deleted file mode 100644 index 9c9c9dd..0000000 --- a/test/e2e-report-stage2.md +++ /dev/null @@ -1,39 +0,0 @@ -# mp2rss-cli 阶段二静态验收报告 - -- 运行时间:2026年05月14日 13:47:52 CST -- 仓库:`/Users/han/coding-agent-workspace/mp2rss-cli` -- HEAD:`8e9c93e docs(readme): rewrite install section for stage-two delivery` - -## 范围说明 - -本报告做的是 **不依赖真实 GitHub Release** 的静态验收。 -真实 Release 验收(push tag → 触发 release.yml → install.sh / npm 真实安装) -需要用户在部署完成后跑,不在本脚本范围内。 - -## 汇总 - -| 指标 | 值 | -|---|---| -| 总步骤 | 6 | -| PASS | 6 | -| FAIL | 0 | -| SKIP | 0 | -| 总耗时 | 3153 ms | - -## 步骤明细 - -| # | 描述 | 命令 | 预期 | 实际 | 结果 | 耗时(ms) | -|---|---|---|---|---|---|---| -| 1/6 | release.yml 语法(actionlint,含 shellcheck 集成) | `actionlint -no-color` | 0 issues | actionlint exit=0; 全部 workflow 0 issues ✓(含 release.yml / ci.yml / release-please.yml) | **PASS** | 153 | -| 2/6 | install.sh 静态(shellcheck + sh -n + dash -n) | `shellcheck -x; sh -n; dash -n` | shellcheck 0 issues;sh / dash 语法解析通过 | shellcheck -x: 0 issues ✓
sh -n: 0 ✓
dash -n: 0 ✓ | **PASS** | 100 | -| 3/6 | npm 包结构(pack --dry-run + 字段检查) | `cd npm && npm pack --dry-run + node 字段检查` | 0 issues,清单无污染,package.json 字段齐全 | npm pack: 4 个文件,dry-run 0
无 node_modules / .DS_Store / __tests__ / .npmrc 污染 ✓
package.json 字段合规:bin / scripts.postinstall / os / cpu / engines / files ✓ | **PASS** | 477 | -| 4/6 | update 端到端 mock(go test httptest fixture) | `go test ./cmd/update/... -run TestLookupChecksum\|TestRunCheck\|TestDoUpdate\|TestSupported` | 5 个测试 PASS:lookup / check up-to-date / check has-update / end-to-end / supported | go test exit=0; 5 个测试 PASS:TestLookupChecksum,TestRunCheck_UpToDate,TestRunCheck_HasUpdate,TestDoUpdate_EndToEnd,TestSupported | **PASS** | 1012 | -| 5/6 | 二进制体积(make build && stat -f%z ≤ 12 MiB) | `go build -trimpath -ldflags='-s -w' -o /tmp/mp2rss-stage2 . && stat -f%z` | size ≤ 12 MiB (12582912 bytes) | size=8734098 bytes (8.33 MiB) ≤ 12582912 bytes (12 MiB) ✓ | **PASS** | 689 | -| 6/6 | CLI update --check 远端 404 友好错误 | `/Users/han/coding-agent-workspace/mp2rss-cli/mp2rss update --check(GitHub releases/latest 当前返回 404)` | 退出非 0;含中文「查询最新版本失败」;无 panic / goroutine 字样 | rc=1, out: 🔎 正在查询最新版本... ✗ 查询最新版本失败:GitHub API HTTP 404 ✓(中文友好,无 panic / 无 stack trace) | **PASS** | 722 | - -## 后续验收(用户在部署阶段做,不在本脚本范围) - -- 通过 release-please 合并版本 PR 后,push tag 触发 release.yml;GitHub Release 应携带 6 个平台二进制 + checksums.txt。 -- 在 macOS / Linux 主机用 `curl -fsSL https://raw.githubusercontent.com/areyoubugcoder/mp2rss-cli/main/scripts/install.sh | sh` 跑一次真实安装。 -- 在 Linux / Windows 主机用 `npm install -g @mp2rss/cli` 跑一次 postinstall。 -- 装好后 `mp2rss update` 验证自更新。 diff --git a/test/e2e-report.md b/test/e2e-report.md deleted file mode 100644 index 4e7e36d..0000000 --- a/test/e2e-report.md +++ /dev/null @@ -1,37 +0,0 @@ -# mp2rss-cli 端到端验证报告 - -- 运行时间:2026年05月22日 13:07:38 CST -- 仓库:`/Users/han/coding-agent-workspace/mp2rss-cli` -- HEAD:`9e3f3e0 docs: 收尾 README / agent-install / SKILL.md 一致性整理` -- API:`http://127.0.0.1:58921`(mock-api 自启 pid=9431) -- 沙箱 HOME:`/var/folders/9v/5lhxqph10r97klzqs3ftd4280000gn/T/mp2rss-cli-e2e-XXXXXX.mKqG796L9x` -- 二进制体积:8825202 bytes(上限 12582912 bytes / 12 MiB) - -## 汇总 - -| 指标 | 值 | -|---|---| -| 总步骤 | 8 | -| PASS | 8 | -| FAIL | 0 | -| SKIP | 0 | -| 总耗时 | 10883 ms | - -## 步骤明细 - -| # | 描述 | 命令 | 预期 | 实际 | 结果 | 耗时(ms) | -|---|---|---|---|---|---|---| -| 1/7 | git identity 校验(必须含 areyoubugcoder) | `git -C /Users/han/coding-agent-workspace/mp2rss-cli config user.email` | 包含子串 areyoubugcoder | 241083775+areyoubugcoder@users.noreply.github.com | **PASS** | 19 | -| 2/7 | 三种登录路径(-k / loopback / --no-browser) | `auth login -k / auth login(loopback+curl 模拟) / echo key \| auth login --no-browser` | 三分支均写 0600 config;loopback callback 200 | 2a -k: 写盘 mode=600 ✓
2b loopback: port=58923 callback=200 落盘 ✓
2c --no-browser stdin: 落盘 ✓ | **PASS** | 399 | -| 3/7 | 核心命令链(subscribe→list→search→articles→remove) | `mp subscribe/list/search/articles/remove --yes -o json \| jq 断言关键字段` | 5 条链路每条退出码 0;JSON camelCase 字段齐;最终 list 已剔除 | 3a subscribe: ok=true articleUrl=匹配 ✓
3b list: items≥1 字段齐 mpId=2234567 ✓
3c search 测试: items≥1 ✓
3d articles 2234567: items≥1 字段齐 ✓
3e remove 2234567 --yes: ok=true mpId 匹配 ✓
3e post-remove list: 不含 mpId=2234567 ✓ | **PASS** | 91 | -| 3.5/7 | X 命令链(subscribe→list→search 异步轮询→posts→articles→remove) | `x subscribe/list/search/posts/articles/remove --yes + --timeout 超时` | 6 条链路+超时分支:JSON 字段齐;search 202→200 轮询完成;--timeout 1ms exit=5 | 3.5a x subscribe: ok=true xUserId=匹配 ✓
3.5b x list: items≥1 字段齐 sourceType=x ✓
3.5c x search elon: status=finished items=2 字段齐 ✓
3.5d x posts 44196397: items≥1 字段齐 ✓
3.5e x articles 44196397: items≥1 字段齐 ✓
3.5f x remove 44196397 --yes: ok=true ✓
3.5g post-remove x list: 不含 xUserId=44196397 ✓
3.5h x search --timeout 1ms: exit=5 ✓ | **PASS** | 1133 | -| 4/7 | 错误路径(401 / 404 / 网络) | `mp list --api-key wrong / mp articles 9999999 / MP2RSS_API_URL=http://127.0.0.1:1 mp list` | 退出码 3/4/5;JSON error envelope code 字段一致 | 4a 错 key: exit=3 error.code=401 ✓
4b 未订阅 mpId: exit=4 error.code=404 ✓
4c 断网: exit=5 error.code=5 ✓ | **PASS** | 560 | -| 5/7 | 质量门(make lint && make test && size ≤ 12 MiB) | `make lint && make test && stat -f%z mp2rss` | lint=0 && test=0 && size ≤ 12 MiB (12582912 bytes) | make lint: 0 ✓
make test: 0 (ok pkgs=9) ✓
binary size=8825202 bytes ≤ 12582912 ✓ | **PASS** | 8113 | -| 6/7 | CHANGELOG 自动化(Conventional Commits 合规 + release-please 注记) | `git log --pretty=%s -20 \| 正则匹配 Conventional Commits` | 全部 commit 符合 (feat\|fix\|docs\|chore\|...): 格式 | Conventional Commits: 19/19 ✓(最近 20 条全合规)
release-please CLI 可用(真正的 dry-run 需在 CI 中带 GITHUB_TOKEN) | **PASS** | 19 | -| 7/7 | 自更新 mock 链路(update_test.go httptest fixture) | `go test ./cmd/update/... -run TestLookupChecksum\|TestRunCheck\|TestDoUpdate\|TestSupported` | 5 个测试 PASS(cover fetchLatest → download → checksum → extract → atomicReplace) | go test exit=0; 0 个测试 PASS(含 TestDoUpdate_EndToEnd 完整端到端 mock) | **PASS** | 549 | - -## 备注 - -- 本报告由 `test/e2e.sh` 自动生成,覆盖 plan「端到端验证(阶段一聚焦)」6 大检查项。 -- 走 mock 时使用 `test/mock-api`(stdlib only),endpoint 与 Open API 契约一致。 -- release-please 真正的 dry-run 需 GitHub token,本地用 Conventional Commits 合规扫描做代理校验;CI 会跑完整版。

AltStyle によって変換されたページ (->オリジナル) /