Folo CLI 实战指南:用 folocli 把 RSS 订阅管理变成可自动化的终端工作流
本文基于仓库中 apps/cli/skill.md 的官方 Skill 定义,系统讲解 Folo CLI(npm 包名 folocli,二进制名 folo)的设计目标、认证机制、输出契约与五大核心工作流。读完后你将掌握:如何用 npx --yes folocli@latest 在任意终端或 Agent 环境中免安装操作 Folo 账号,如何解读其稳定的 JSON 输出信封与分页游标,并结合仓库源码理解登录回调、Token 解析优先级与错误恢复的实现原理。
一、Folo CLI 是什么:一个面向 Agent 的终端技能定义
skill.md 并非普通 README,而是一份面向 AI Agent 的"技能说明书"(Skill),它明确了触发条件、前置要求、执行策略、输出契约与命令参考。从 package.json 可以看到,该包被显式声明为随 npm 分发的文件之一("files": ["dist", "skill.md"]),这意味着 Agent 在执行 npx folocli 时能直接获取这份行为规范——这是"以文档驱动自动化"的典型工程做法。
触发条件
当用户提出以下类型的请求时,应使用 Folo CLI 技能:
- 管理 RSS 订阅(增删改查订阅、列表)
- 浏览时间线条目
- 读取条目详情或 readability 正文
- 标记条目已读/未读
- 搜索 feeds/列表或热门源(trending)
- 导入/导出 OPML
- 查询未读数量
执行策略(Execution Policy)
skill.md 给出的执行策略对自动化场景非常关键,可逐条落实为脚本约定:
- 所有 Agent 执行优先使用
npx --yes folocli@latest ...:不需要npm install -g folocli; - 无需单独的 update preflight:使用
folocli@latest本身就是更新策略; - 如果用户机器上已有可用的全局
folo二进制,也可以接受,但文档与自动化中推荐默认仍是npx --yes folocli@latest。
这一策略在 package.json 中有对应事实支撑:bin 字段将 folo 映射到 ./dist/index.js,engines 要求 Node.js >= 18。
二、前置条件与认证体系
前置条件
- 安装 Node.js 与 npm,保证 CLI 可以通过
npx执行; - 完成认证配置,三选一:
# 推荐:打开浏览器自动登录
npx --yes folocli@latest login
# 方式二:直接提供 session token
npx --yes folocli@latest login --token <session-token>
# 方式三:设置环境变量
export FOLO_TOKEN=<token>
Token 解析优先级(源码级验证)
从 src/client.ts 的 createCommandContext 可以看到,Token 与 API 地址的解析遵循严格的优先级链:
token: --token 全局参数 > FOLO_TOKEN 环境变量 > ~/.folo/config.json 中存储的 token
apiUrl: --api-url 参数 > config.json 中的 apiUrl > 默认值 https://api.folo.is
默认 API 地址定义于 src/client.ts:
export const defaultApiURL = "https://api.folo.is"
凭证持久化在用户主目录的 ~/.folo/config.json 中,仅保存 token 与 apiUrl 两个字段;logout 会调用 clearToken 只清除 token、保留 apiUrl。注意源码中的 normalizeToken 会对包含 % 的 token 做一次 decodeURIComponent,这是为了兼容从 URL 回调中捕获的 percent-encoded token。
浏览器登录的底层流程
login 不带 --token 时走"本地回调服务器 + 一次性令牌"流程,实现见 src/browser-login.ts:
- 在
127.0.0.1上启动一个随机端口的本地 HTTP 服务,监听/callback路径; - 按 src/browser-login.ts 的
resolveCLILoginUrl,把登录页解析为 Web 域下的/login?cli_callback=http://127.0.0.1:<port>/callback(prod/dev/local 三套 API 与 Web 域名映射见同文件mappedWebOrigins); - 用平台命令打开浏览器(macOS
open、Windowscmd /c start、Linuxxdg-open);打开失败会抛出BROWSER_OPEN_FAILED并打印手动打开的 URL; - 用户在浏览器完成登录后,一次性 token 通过回调带回本地服务器,CLI 依次调用
/better-auth/one-time-token/apply(404 时回退/better-auth/one-time-token/verify)换取正式 session token(src/browser-login.ts); - 最后用
/better-auth/get-session校验 token 有效(要求返回体同时含user与session),成功后将 token 与 apiUrl 写入~/.folo/config.json。
浏览器登录默认等待 180 秒,可用 --timeout <seconds> 调整(正整数,见 src/commands/auth.ts);超时抛出 TIMEOUT 错误,提示重新执行 login。
登录成功后输出示例(来自 runLoginAction):
{
"ok": true,
"data": {
"message": "Login successful.",
"configPath": "/home/<user>/.folo/config.json",
"user": { "…": "session 中的用户对象" }
},
"error": null
}
whoami(等价于 auth whoami)则调用 get-session 并返回 user、session、role、roleEndAt、feedSubscriptionLimit、rsshubSubscriptionLimit 等字段(src/commands/auth.ts),可用于在脚本中确认当前账号与配额。
三、输出契约(Output Contract)与格式切换
所有命令默认输出 JSON,使用稳定信封:
{
"ok": true,
"data": {},
"error": null
}
失败时:
{
"ok": false,
"data": null,
"error": {
"code": "UNAUTHORIZED",
"message": "Token is invalid or expired."
}
}
这个契约由 src/output.ts 的 printSuccess/printFailure 实现:成功写入 stdout,失败写入 stderr 并把 process.exitCode 置 1(见 src/command.ts 的 runCommand 统一包装)。对脚本而言,"先判 ok 再取 data"即可安全解析。
错误码由 normalizeError 归一化:
| 错误码来源 | code | 典型场景 |
|---|---|---|
FollowAuthError |
UNAUTHORIZED |
token 无效/过期、未登录 |
FollowAPIError |
服务端 code,缺省为 HTTP_<status> |
4xx/5xx 响应 |
| 本地参数错误 | INVALID_ARGUMENT |
非法参数值、互斥参数冲突 |
| 浏览器登录 | TIMEOUT / BROWSER_OPEN_FAILED |
回调超时、打不开浏览器 |
| 网络层 | NETWORK_ERROR |
请求本身发起失败 |
| 兜底 | UNKNOWN_ERROR |
其他未分类异常 |
格式切换通过全局参数 -f, --format(也写作 --format):
--format json(默认,Agent/脚本首选)--format table:自实现的 ASCII 表格渲染(renderAsciiTable)--format plain:util.inspect的人类可读输出
JSON 序列化时对 bigint 做了字符串化处理(stringifyJSON),避免 ID 溢出精度问题。
四、五大核心工作流
以下命令均来自 skill.md 的 Core Workflows,可直接复制执行。
1. 时间线阅读(Timeline Reading)
# 拉取时间线(--limit 默认 20)
npx --yes folocli@latest timeline --limit 10
# 获取条目详情
npx --yes folocli@latest entry get <entryId>
# 获取 readability 正文
npx --yes folocli@latest entry read <entryId>
timeline 的完整参数(见 src/commands/timeline.ts):
--view <type>:视图类型,合法取值articles(0) | social(1) | pictures(2) | videos(3) | audio(4) | notifications(5),也接受数字(解析逻辑在 src/args.ts);--limit <n>:正整数,默认 20;--unread-only:只取未读条目(底层映射为read: false);--cursor <datetime>:分页游标,ISO 日期时间,内部映射为publishedAfter并做 ISO 归一化(parseISODate);--feed <feedId>/--list <listId>/--category <name>:三者互斥,同时传多个会抛INVALID_ARGUMENT(src/commands/timeline.ts)。其中--category会先拉取订阅列表,筛选出该分类下的所有 feedId 再查时间线;若分类为空则直接返回空结果。
2. 订阅管理(Subscription Management)
# 发现源(发现页检索,默认 feeds,--type lists 可切到列表)
npx --yes folocli@latest search discover <keyword>
# 按 URL 添加订阅
npx --yes folocli@latest subscription add --feed <url>
# 或按列表 ID 批量添加
npx --yes folocli@latest subscription add --list <listId>
# 查看订阅
npx --yes folocli@latest subscription list
subscription add 支持 --category <name>、--view <type>、--private;subscription remove <id> 支持 --target feed|list|url 区分目标类型;subscription update <id> 可改分类、标题、视图与私有性(--private/--public)。
3. 未读处理(Unread Processing)
# 未读总数
npx --yes folocli@latest unread count
# 未读订阅列表
npx --yes folocli@latest unread list
# 只读未读条目
npx --yes folocli@latest timeline --unread-only --limit 20
# 标记单条已读
npx --yes folocli@latest entry mark-read <entryId>
# 批量按当前视图标记全部已读
npx --yes folocli@latest entry mark-all-read --view articles
entry mark-all-read 支持 --feed <feedId> / --list <listId> 缩小范围(两者互斥)与 --view <type> 限定视图;entry mark-unread <entryId> 可把条目改回未读。实现见 src/commands/entry.ts。
4. 收藏操作(Collection Operations)
npx --yes folocli@latest collection add <entryId>
npx --yes folocli@latest collection remove <entryId>
npx --yes folocli@latest collection list --limit 20
collection list 支持 --limit 与 --cursor <datetime> 分页;collection add 支持 --view <type>。
5. OPML 导入 / 导出
# 导出订阅到 OPML 文件
npx --yes folocli@latest opml export --output backup.opml
# 从 OPML 文件导入
npx --yes folocli@latest opml import feeds.opml
从 src/commands/opml.ts 可见两个实现细节:
opml export不带--output时直接返回服务端响应;带--output时会创建父目录、写入文件,并返回{ output, filename, contentType, bytes }摘要;opml import以multipart/form-data上传文件(file字段),--items <url1,url2,...>会被解析成 JSON 数组后以items字段附带,用于指定只导入文件中的部分 feed URL。
五、分页模式(Pagination Pattern)
timeline 的返回结构包含三个分页字段:
entries:本批条目nextCursor:下一页游标hasNext:是否还有下一页
从 src/commands/timeline.ts 的实现看,游标就是最后一条条目的 publishedAt,hasNext 的判定为"存在 nextCursor 且本批条数达到 --limit":
const nextCursor = entries.at(-1)?.entries.publishedAt ?? null
return {
entries,
nextCursor,
hasNext: Boolean(nextCursor) && entries.length >= options.limit,
}
因此标准遍历循环是:
# 1. 首页
npx --yes folocli@latest timeline --limit 20
# 2. 从响应中读 nextCursor
npx --yes folocli@latest timeline --limit 20 --cursor <nextCursor>
# 3. 直到 hasNext 为 false
在脚本中可用 jq 组合:... timeline --limit 20 | jq -r '.data.nextCursor' 取下一页游标,.data.hasNext 判停。
六、完整命令参考(Command Reference)
以下为 skill.md 中的完整命令清单,按功能分组:
认证(顶层命令与 auth 子命令等价)
npx --yes folocli@latest login [--timeout <seconds>] [--token <token>]npx --yes folocli@latest logoutnpx --yes folocli@latest whoaminpx --yes folocli@latest auth login [--timeout <seconds>] [--token <token>]npx --yes folocli@latest auth logoutnpx --yes folocli@latest auth whoami
时间线
npx --yes folocli@latest timeline [--view <type>] [--limit <n>] [--unread-only] [--cursor <datetime>]npx --yes folocli@latest timeline --feed <feedId> [--limit <n>] [--cursor <datetime>]npx --yes folocli@latest timeline --list <listId> [--limit <n>] [--cursor <datetime>]npx --yes folocli@latest timeline --category <name> [--view <type>] [--limit <n>]
订阅
npx --yes folocli@latest subscription list [--view <type>] [--category <name>]npx --yes folocli@latest subscription add --feed <url> [--category <name>] [--view <type>] [--private]npx --yes folocli@latest subscription add --list <listId> [--category <name>] [--view <type>]npx --yes folocli@latest subscription remove <id> [--target feed|list|url]npx --yes folocli@latest subscription update <id> [--target feed|list] [--category <name>] [--title <title>] [--view <type>] [--private|--public]
条目
npx --yes folocli@latest entry get <entryId>npx --yes folocli@latest entry read <entryId>npx --yes folocli@latest entry mark-read <entryId>npx --yes folocli@latest entry mark-unread <entryId>npx --yes folocli@latest entry mark-all-read [--feed <feedId>] [--list <listId>] [--view <type>]
Feed
npx --yes folocli@latest feed get <feedId|feedUrl>npx --yes folocli@latest feed refresh <feedId>npx --yes folocli@latest feed analytics <feedId>
列表(List)
npx --yes folocli@latest list lsnpx --yes folocli@latest list get <listId>npx --yes folocli@latest list create --title <title> [--description <desc>] [--view <type>] [--fee <n>]npx --yes folocli@latest list update <listId> [--title <title>] [--description <desc>] [--view <type>] [--fee <n>]npx --yes folocli@latest list delete <listId>npx --yes folocli@latest list add-feed <listId> --feed <feedId>npx --yes folocli@latest list remove-feed <listId> --feed <feedId>
搜索
npx --yes folocli@latest search discover <keyword> [--type feeds|lists]npx --yes folocli@latest search rsshub <keyword> [--lang <lang>]npx --yes folocli@latest search trending [--range 1d|3d|7d|30d] [--view <type>] [--limit <n>] [--language eng|cmn] [--category <keyword>]
收藏
npx --yes folocli@latest collection list [--limit <n>] [--cursor <datetime>]npx --yes folocli@latest collection add <entryId> [--view <type>]npx --yes folocli@latest collection remove <entryId>
OPML
npx --yes folocli@latest opml export [--output <file>]npx --yes folocli@latest opml import <file> [--items <url1,url2,...>]
未读
npx --yes folocli@latest unread countnpx --yes folocli@latest unread list [--view <type>]
七、错误恢复(Error Recovery)
skill.md 给出了三类标准错误的恢复路径,均可从源码印证:
UNAUTHORIZED
token 无效、过期或未登录时(对应 client.ts 中 Missing token 与 fetchAuthSession 的会话校验失败):
npx --yes folocli@latest login
# 或
npx --yes folocli@latest login --token <token>
# 或设置 FOLO_TOKEN
HTTP_4xx / HTTP_5xx
- 加
--verbose重试,查看请求细节。--verbose会挂接请求/响应拦截器,把[request] METHOD URL与[response] METHOD URL -> status打印到 stderr(setupVerboseLogging); - 若使用非默认端点,核对
--api-url是否正确。
INVALID_ARGUMENT
执行 <command> --help 检查该命令接受的参数。常见的本地参数校验错误包括:--view 传了非法值(提示合法取值表)、--limit 非正整数、timeline 中 --feed/--list/--category 同时出现等(src/args.ts 与 timeline.ts)。
八、可验证性:测试与入口
CLI 的行为有对应测试覆盖,便于读者进一步验证本文结论:
- src/cli.e2e.test.ts:端到端命令流程;
- src/browser-login.test.ts:浏览器登录回调与一次性令牌流程;
- src/auth-command.test.ts:login/logout/whoami 命令;
- src/output.test.ts 与 src/args.test.ts:输出信封、表格渲染与参数解析;
- src/index.ts:入口,注册全部命令组与全局参数(
-f/--format、--api-url、--token、--verbose),并在解析异常时统一走printFailure保持 JSON 信封。
小结
Folo CLI 通过三层设计让 RSS 管理进入脚本与 Agent 工作流:稳定的 JSON 输出信封让程序可以可靠解析结果;npx folocli@latest + ~/.folo/config.json / FOLO_TOKEN 的凭证链让免安装、多环境执行成为可能;游标式分页与互斥参数校验则把复杂的时间线检索约束成了可循环、可恢复的调用模式。skill.md 定义了"做什么、按什么契约做",而 apps/cli/src 下的实现给出了"为什么这样做"的源码答案,两者结合即可在 CI、个人脚本或 AI Agent 中构建完整的 Folo 自动化管线。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00