baoyu-url-to-markdown
Fetch any URL and convert to markdown using baoyu-fetch CLI (Chrome CDP with site-specific adapters). Built-in adapters for X/Twitter, YouTube transcripts, Hacker News threads, and generic pages via Defuddle. Handles login/CAPTCHA via interaction wait modes. Use when user wants to save a webpage as markdown.
1How It Works & Mechanism
The entry point is a shell wrapper + vendored TS CLI: baoyu-fetch directly runs lib/cli.ts via bun, and the CLI selects an adapter (x / youtube / hn / generic) based on the URL.
exec bun "$script_dir/lib/cli.ts" "$@"
Scraping relies on real Chrome + a custom-built CDP client (launches browser via chrome-launcher and connects directly to DevTools via ws), rather than puppeteer.
import WebSocket from "ws";
The X adapter does not merely read the DOM; it features a built-in session mechanism: it first reads x.com/twitter.com cookies via CDP (requiring auth_token and ct0), writes the cookies in plaintext into a sidecar file under the profile directory, and re-injects them on subsequent runs.
await writeFile(filePath, JSON.stringify(data, null, 2));
The data source for X comes from the page's own GraphQL responses: the tool extracts and parses /graphql/ response bodies such as TweetDetail and TweetResultByRestId from CDP network logs, and automatically scrolls and clicks 'Show replies' to load thread content.
entry.url.includes("TweetDetail") ||
The YouTube adapter uses custom-built InnerTube calls: it extracts INNERTUBE_API_KEY from ytcfg on the page, sends a POST to /youtubei/v1/player within the page context (credentials: 'include', carrying page cookies), and explicitly impersonates an ANDROID client.
context: { client: { clientName: 'ANDROID', clientVersion: '20.10.38' } },
Can reuse an existing running Chrome debugging session: scans ps aux first for processes with the same profile and --remote-debugging-port, or reads the DevToolsActivePort file in the profile, and attaches upon discovery.
const activePort = parseDevToolsActivePort(path.join(options.profileDir, "DevToolsActivePort"));
When encountering login walls or CAPTCHAs, it avoids brute-forcing and provides wait modes: when Cloudflare/reCAPTCHA or similar challenges are detected on the page, it pops up visible Chrome for manual user solving, resuming upon detecting progress or on pressing Enter.
text.includes("verify you are human") ||
The primary pipeline for generating markdown relies on local Defuddle/Readability + quality scoring; only when quality falls short is the target URL sent to the third-party service https://defuddle.md to obtain cleaner markdown.
const DEFUDDLE_API_ORIGIN = "https://defuddle.md";
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| package | @mozilla/readability ^0.6.0(正文提取) |
| package | defuddle ^0.17.0(本地 markdown 提取) |
| package | jsdom ^30.0.1(HTML 清洗/解析,未开 runScripts) |
| package | turndown + turndown-plugin-gfm(HTML→markdown) |
| package | chrome-launcher ^1.2.1(浏览器启动) |
| package | ws ^8.18.3(CDP WebSocket 客户端) |
| package | unified + remark-parse + remark-gfm(媒体链接收集与重写) |
| cli | bun(CLI 运行时;shell 包装 exec bun) |
| cli | Chrome/Chromium 二进制(chrome-launcher,可用 --browser-path 指定) |
| cli | ps aux(POSIX 进程扫描)与 osascript(macOS 窗口激活) |
| network | Chrome DevTools HTTP/WebSocket 端点 |
| api | 第三方 markdown 服务 defuddle.md(质量不足时把目标 URL 发过去) |
| network | YouTube watch 页、InnerTube player、caption 轨与 i.ytimg.com 缩略图 |
| network | Hacker News 条目页(jsdom 解析) |
| network | X/Twitter 页面与页面自身发出的 /graphql/ 请求(工具只观察,不构造请求) |
| network | 用户指定 URL 与页面内媒体 URL(下载时带伪造的桌面 Chrome UA) |
4Recommended Governance & Evidence
- SKILL.md / 脚本: Calls undocumented or reverse-engineered private APIs / spoofs client identifiers
- SKILL.md / 脚本参数: Contains logic to bypass automation detection, disable host sandboxing, or skip SSL certificate validation
- SKILL.md / 会话存储: Reads or reuses browser user login states and session cookies
- 本地凭据 / 浏览器配置: Reads user login cookies/sessions/keyrings: reads x.com/twitter.com cookies via CDP Network.getCookies, requiring auth_token and ct0 to be present
- scripts/: Executes local scripts or commands: {baseDir}/scripts/baoyu-fetch <url> … (shell exec bun → lib/cli.ts; requires bun i on first run)
- 网络端点: Declares external network access: user-specified target URLs (real browser pages; generic pages routed through Readability/Defuddle/defuddle.md)
- 本地工作目录: File read/write and artifact output: markdown/JSON specified by --output (parent directories created automatically)
5Second-Pass Verification
- [ok] YouTube 适配器的逆向/冒充 — transcript.ts 有 window.ytcfg?.data_?.INNERTUBE_API_KEY、页面内 POST /youtubei/v1/player?key=…(credentials: 'include')与 context.client.clientName='ANDROID' 三处,与 Main 终裁的『逆向 youtubei + 冒充官方客户端』一致。
- [ok] X cookie 明文 sidecar 落盘 — cookie-sidecar.ts 用 Network.getCookies 取 cookie 后 JSON.stringify 明文写入 <profile>/x-session-cookies.json(x/index.ts 指定 filename),无加密无 chmod;convert.ts 在开头尝试回灌、finally 里导出。
- [ok] 接管已运行调试会话 — profile.ts 解析 DevToolsActivePort 并 spawnSync('ps', ['aux']) 匹配 profileDir + --remote-debugging-port;chrome-launcher.ts 支持 --cdp-url ws:// 直连与复用日志 'Reusing Chrome debugger at …'。
- [ok] 网络日志含请求头与响应体(--debug-dir 落盘) — network-journal.ts 记录 requestHeaders / requestBody / 响应体(Network.getResponseBody),convert.ts 在写 debug 前调用 toJSON({ includeBodies: true }) 并写入 network.json。
- [ok] 第三方 defuddle.md 兜底 — DEFUDDLE_API_ORIGIN=https://defuddle.md,构建 URL 后 fetch;通用适配器仅在 outputFormat==='markdown' 时启用该兜底,JSON 模式不会外发目标 URL。
- [discrepancy] 文档与实现不一致(默认无头) — SKILL.md 第 93 行写 '# Default: headless capture, markdown to stdout'、quality-gate.md 亦写 'Default | Headless Chrome',但 cli.ts 默认 headless: false(convert.ts 在非交互模式传该值),实际会启动可见 Chrome(--no-startup-window)。使用前应知道默认并非无头。
- [discrepancy] 文档与实现不一致(默认 profile 路径) — SKILL.md 写默认 ./baoyu-skills/chrome-profile,代码实际解析为按 OS 的绝对路径(XDG_DATA_HOME / Application Support / APPDATA 下的 baoyu-skills/chrome-profile),从不使用 cwd 相对路径。
- [discrepancy] 文档未披露 cookie 导出 — SKILL.md 只写 BAOYU_CHROME_PROFILE_DIR 为『Chrome user data directory』,从未提及会导出 X cookie 到 sidecar 文件;该行为只在代码中可见——这是本次侦查中风险披露落差最大的一处。
6Verdict & Scenarios
15be54497a8d6c30…