In Claude Code in any project, "throw in a Feishu link → it lands in a local directory". This skill does not directly request Feishu; instead it hands the task to the already logged-in feishu2md browser extension(which holds the cookie and the export engine).
CC ──fetch.mjs(一次性)──HTTP /command──▶ daemon ──WebSocket──▶ Chrome 扩展
▲ 127.0.0.1:19925 └ 后台开标签页跑导出
└ 流式回传进度/产物 ──▶ 解包到 <输出目录>/<文档名>/
This skill is self-contained:daemon and the protocol code is packaged with the skill in scripts/bridge/ in,fetch.mjs what is started is
the daemon inside the skill,does not depend on the directory structure of the feishu2md repository, so it can be called from any project. The only external dependency is
that extension installed in Chrome and already logged in to Feishu (the extension cannot be stuffed into the skill; it can only be loaded once in the browser, see "First-time installation").
Each document lands in its own subfolder(named after the document title), not tiled and mixed together with other files:
<输出目录>/
└── 无监督数据修复/ ← 文档标题命名的文件夹
├── 无监督数据修复.md
└── images/ ← 图片用相对路径内联引用
└── xxx.png
- The extension is a WS client and actively connects outto the local daemon — no native messaging, no system-level installation.
- The daemon is automatically started on demand by this skill, persists (auto-exits after 30 minutes idle), binds only
127.0.0.1。 - fetch.mjs is one-shot: exits once it finishes, does not stay resident.
Usage
node ~/.claude/skills/feishu-doc-fetch/scripts/fetch.mjs <飞书链接> <输出目录> [--format md|pdf|html] [--profile <code>]
<输出目录>is theparent directory; the output will land in<输出目录>/<文档名>/subfolder (each document gets its own separate folder, so they don't get mixed up with each other).--formatDefaultmd。md/htmlwill download images and inline them into that subfolder by relative pathimages/;pdfdepends on tenant capabilities.- Progress goes to stderr, the result path goes to stdout (
✓ 已导出到followed by the document's subfolder absolute path). --profile <code>: when multiple browser profiles are connected to the daemon at the same time, specify which one to use (the code is shown in the Profile in the extension popup, and you can click Copy to copy it). When there is only one, no need to specify it.
Exit codes:0 success | 1 failure | 2 usage error | 3 login required | 4 domain authorization required | 5 bridge not ready.
Execution flow (CC operates according to this)
- Directly run the command above (the link given by the user + the local directory specified by the user; if the user did not specify a directory, it defaults to the current working directory). The daemon will be started automatically.
- Check the exit code/output and handle it as follows, do not guess and retry on your own:
5bridge not ready → the daemon cannot start or the extension is not connected. Confirm that Chrome is open and the extension is loaded (see "First-time installation"), click the extension icon once to wake up the background, then retry.3login required → have the user log in to Feishu in Chrome, then retry.4domain authorization required → a privatized deployment domain requires user gesture authorization: have the user open any Feishu page under that domain → click the extension icon to open the sidebar → click "Authorize this domain", then retry.0→ tell the user the written files listed in stdout (the path looks like<文档名>/<文档名>.md)。
- On success the output is in
<输出目录>/<文档名>/subfolder (mdcontainsimages/subdirectories referenced with relative paths), each document is stored in its own folder.
First-time installation (only once, you only need to load the extension, no system-level installation)
The extension comes from the feishu2md repository (this skill does not include the extension itself, only the bridge daemon):
- In the feishu2md repository root
npm run build。 - to
chrome://extensions, enable "Developer mode" → "Load unpacked extension" and selectdist/。 - Click the extension icon once to wake up the background Service Worker — it will automatically connect to the local daemon (the daemon is pulled up by the first
fetch.mjs).
No need to bind an extension ID or write any system manifest. To switch machines, just repeat these three steps (then copy this skill directory to the new machine's
~/.claude/skills/)。
Troubleshooting
~/.feishu2md/daemon.logto check whether the daemon is listening, whether the extension is connected, and whether tasks are being dispatched.- Cannot connect: confirm Chrome is open and the extension is loaded; when the extension background is asleep, the first command may need to wait for it to reconnect via
/ping(alarms ~24s), or click the extension icon to wake it immediately. - Port conflict: the daemon defaults to
127.0.0.1:19925, and can be changed with the environment variableFEISHU2MD_PORT(it must match thesrc/background/bridge.tsin the extension'sPORT). - Missing images/export failure for a domain: usually because that domain was not authorized under the "base domain wildcard" (including the image drive-stream subdomain); reauthorize it in the sidebar.
- Security: the daemon only binds to loopback, verifies Origin (rejecting non-
chrome-extension://)+ requires theX-Feishu2mdheader, blocking CSRF initiated by web pages; other local processes can still connect (an inherent risk of loopback, acceptable for a self-use tool).