1实现原理 · 为什么它能做到
核心是逆向 X 的 GraphQL 接口:先抓 x.com 页面 HTML 里的 queryId 与 featureSwitches,再把变量拼进 /i/api/graphql/<queryId>/<Operation> 请求。
const url = new URL(`https://x.com/i/api/graphql/${queryInfo.queryId}/TweetResultByRestId`);
请求带的是 X 网页端公开 bearer token 与固定 UA——这是“冒充官方 Web 客户端”的实现手段,也是它不需要申请 API Key 的原因。
export const DEFAULT_BEARER_TOKEN =
鉴权靠 cookie:优先读环境变量 X_AUTH_TOKEN / X_CT0(可加 X_GUEST_TOKEN / X_TWID),其次本地 cookie 文件,最后经 CDP 从 Chrome 读。
const authToken = process.env.X_AUTH_TOKEN?.trim();
抓到的推文/文章被转成 markdown:线程按 TweetDetail 的会话结构串联,Article 走 articleEntity 全文,引用推文另有解析,最终产出带 YAML front matter 的文件。
**File structure**: `x-to-markdown/{username}/{tweet-id}/{content-slug}.md`
媒体本地化是可选步骤:开启 --download-media 后下载图片/视频到 markdown 同级 imgs/ 与 videos/,并把链接改写为本地相对路径。
if (hostname.includes("video.twimg.com")) return "video";
同意门禁是硬要求:没有 consent.json(accepted + disclaimerVersion 1.0)时必须先向用户展示免责声明并取得同意,拒绝即退出。
This tool uses a reverse-engineered X API, NOT official.
首次设置同样强制:EXTEND.md 不存在时必须用交互工具问偏好(媒体处理策略、默认输出目录、保存位置),明确禁止静默写默认值。
**CRITICAL**: When EXTEND.md is not found, you **MUST use `AskUserQuestion`** to ask the user for their preferences before creating EXTEND.md.
媒体处理默认“先出 markdown 再问”:先不带下载参数跑一次,检查产出里是否还有远端媒体 URL,有才询问是否下载并二次运行覆盖链接。
5. If user confirms → run script **again** with `--download-media` (overwrites markdown with localized links)
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| network | x.com(GraphQL /i/api/graphql/… 与首页 HTML 抓 queryId) |
| network | pbs.twimg.com(图片媒体下载) |
| network | video.twimg.com(视频媒体下载) |
| package | baoyu-chrome-cdp ^0.1.1(npm,CDP 取 cookie) |
| cli | bun / npx(${BUN_X} 运行时) |
4风险提醒 风险提醒:红色 · 谨慎使用
- 账号风险(逆向 API + 冒充网页客户端) — 带硬编码 bearer 与固定 UA 请求私有 GraphQL,README 明确可能 'Account restrictions possible if API usage detected';建议用小号或专用 cookie。
- cookie 处理与存放 — auth_token/ct0 会被读入内存并入请求头;脚本会把查到的 cookie 缓存/使用(本地文件与 CDP 两条路),这些值等价于账号登录态。
- 文本与媒体外发/落盘 — 推文正文原样写成 markdown(可能含跟踪链接与远端媒体 URL);--download-media 会从 pbs/video.twimg.com 拉文件到本地,需注意磁盘与来源可信度。
- 输出路径由远端数据参与决定 — 目录用 username/tweet-id/content-slug 命名,username 来自抓取结果;异常字符或非常规用户名可能导致路径不如预期(建议必要时用 -o 固定)。
- 接口随时可能失效 — queryId/featureSwitches 抓取失败会回落到常量,常量过期即整体失败;无官方支持渠道。
- 内容合规 — 把 X 内容抓成本地归档涉及转载/版权与平台条款,使用者需自行确认授权范围。
5第二遍独立确认
- [ok] 逆向 GraphQL 请求 + 客户端冒充(red 主因) — graphql.ts 从首页 HTML 抓 queryId/featureSwitches,拼 https://x.com/i/api/graphql/${queryId}/Operation;constants.ts 提供 FALLBACK_QUERY_ID 与硬编码 bearer/UA。
- [ok] 浏览 cookie 读取 — cookies.ts:buildInlineCookiesFromEnv 读 X_AUTH_TOKEN/X_CT0/X_GUEST_TOKEN/X_TWID;loadXCookiesFromFile 读本地文件;loadXCookiesFromCdp 经 CDP 取;hasRequiredXCookies 校验 auth_token 与 ct0。
- [ok] 媒体下载范围 — media-localizer.ts 仅对 pbs.twimg.com / video.twimg.com 判定类型并 fetch 下载,且只在 --download-media 路径调用(SKILL.md 的三态策略)。
- [ok] 两道用户门禁 — SKILL.md:consent.json(accepted + disclaimerVersion 1.0)与 EXTEND.md 首次设置(三问,禁止静默默认),后者被单独标为 BLOCKING。
- [ok] 无 TLS 降级 / 无 stealth 伪装 / 无任意代码执行 — 相关 token 全目录零命中;入口是单个 URL;无 eval/动态 import 远程模块。
- [ok] 与 baoyu-post-to-x 的路线差异(避免档位观感不一致) — post-to-x 走真实 UI + CDP 自动化(另行定级);本 skill 走私有 API 伪造请求 → red。差异来源是技术路线,已在 reason 写明。
- [ok] meta 元数据 — GitHub API:MIT、25926 stars、pushed_at 2026-09-10T15:13:43Z;本地 HEAD 与 pin 1567581c26ec… 一致。
6结论
bb27420994157cc5…1567581c26