首页
/ Folo CLI 实战指南:用 folocli 把 RSS 订阅管理变成可自动化的终端工作流

Folo CLI 实战指南:用 folocli 把 RSS 订阅管理变成可自动化的终端工作流

2026-09-05 23:43:01作者:庞眉杨Will

本文基于仓库中 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.jsengines 要求 Node.js >= 18。

二、前置条件与认证体系

前置条件

  1. 安装 Node.js 与 npm,保证 CLI 可以通过 npx 执行;
  2. 完成认证配置,三选一:
# 推荐:打开浏览器自动登录
npx --yes folocli@latest login

# 方式二:直接提供 session token
npx --yes folocli@latest login --token <session-token>

# 方式三:设置环境变量
export FOLO_TOKEN=<token>

Token 解析优先级(源码级验证)

src/client.tscreateCommandContext 可以看到,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 中,仅保存 tokenapiUrl 两个字段;logout 会调用 clearToken 只清除 token、保留 apiUrl。注意源码中的 normalizeToken 会对包含 % 的 token 做一次 decodeURIComponent,这是为了兼容从 URL 回调中捕获的 percent-encoded token。

浏览器登录的底层流程

login 不带 --token 时走"本地回调服务器 + 一次性令牌"流程,实现见 src/browser-login.ts

  1. 127.0.0.1 上启动一个随机端口的本地 HTTP 服务,监听 /callback 路径;
  2. src/browser-login.tsresolveCLILoginUrl,把登录页解析为 Web 域下的 /login?cli_callback=http://127.0.0.1:<port>/callback(prod/dev/local 三套 API 与 Web 域名映射见同文件 mappedWebOrigins);
  3. 用平台命令打开浏览器(macOS open、Windows cmd /c start、Linux xdg-open);打开失败会抛出 BROWSER_OPEN_FAILED 并打印手动打开的 URL;
  4. 用户在浏览器完成登录后,一次性 token 通过回调带回本地服务器,CLI 依次调用 /better-auth/one-time-token/apply(404 时回退 /better-auth/one-time-token/verify)换取正式 session token(src/browser-login.ts);
  5. 最后用 /better-auth/get-session 校验 token 有效(要求返回体同时含 usersession),成功后将 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 并返回 usersessionroleroleEndAtfeedSubscriptionLimitrsshubSubscriptionLimit 等字段(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.tsprintSuccess/printFailure 实现:成功写入 stdout,失败写入 stderr 并把 process.exitCode 置 1(见 src/command.tsrunCommand 统一包装)。对脚本而言,"先判 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 plainutil.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_ARGUMENTsrc/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>--privatesubscription 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 importmultipart/form-data 上传文件(file 字段),--items <url1,url2,...> 会被解析成 JSON 数组后以 items 字段附带,用于指定只导入文件中的部分 feed URL。

五、分页模式(Pagination Pattern)

timeline 的返回结构包含三个分页字段:

  • entries:本批条目
  • nextCursor:下一页游标
  • hasNext:是否还有下一页

src/commands/timeline.ts 的实现看,游标就是最后一条条目的 publishedAthasNext 的判定为"存在 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 logout
  • npx --yes folocli@latest whoami
  • npx --yes folocli@latest auth login [--timeout <seconds>] [--token <token>]
  • npx --yes folocli@latest auth logout
  • npx --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 ls
  • npx --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 count
  • npx --yes folocli@latest unread list [--view <type>]

七、错误恢复(Error Recovery)

skill.md 给出了三类标准错误的恢复路径,均可从源码印证:

UNAUTHORIZED

token 无效、过期或未登录时(对应 client.tsMissing tokenfetchAuthSession 的会话校验失败):

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.tstimeline.ts)。

八、可验证性:测试与入口

CLI 的行为有对应测试覆盖,便于读者进一步验证本文结论:

小结

Folo CLI 通过三层设计让 RSS 管理进入脚本与 Agent 工作流:稳定的 JSON 输出信封让程序可以可靠解析结果;npx folocli@latest + ~/.folo/config.json / FOLO_TOKEN 的凭证链让免安装、多环境执行成为可能;游标式分页与互斥参数校验则把复杂的时间线检索约束成了可循环、可恢复的调用模式。skill.md 定义了"做什么、按什么契约做",而 apps/cli/src 下的实现给出了"为什么这样做"的源码答案,两者结合即可在 CI、个人脚本或 AI Agent 中构建完整的 Folo 自动化管线。

登录后查看全文
热门项目推荐
相关项目推荐