创见博客
为 AI 写作工作流打造 Visionary CLI:架构与设计思路
七崽爱吃小饼干2026/07/23阅读 2

随着 Agent 辅助编写文章越来越方便,内容生产的入口正在从纯手工编辑器,逐渐扩展到“人提出方向,AI 生成、修改、整理和发布”的工作流。CLI 工具天然适合 AI 调用:参数明确、输出结构化、执行路径稳定,也容易嵌入自动化流程。因此我为 Visionary 搭建了一套 CLI 工具,让 AI 不只负责写文章,也能通过命令完成创建草稿、更新内容、发布文章和管理专栏等操作。

现在这套工具已经整理成 npm 包,可以直接全局安装使用:

bash
npm install -g visionary-cli

GitHub 仓库地址:

text
https://github.com/PassingTraveller111/visionary-cli

这篇文章记录 Visionary CLI 当前的架构、设计取舍和功能边界。

目标:把发布动作变成稳定的命令

Visionary CLI 的核心目标很明确:让内容发布和内容管理可以通过命令完成。

安装后,可以直接在任意目录执行:

bash
visionary --help
visionary auth login --username <username> --password <password>
visionary draft create --title <title> --content-file ./post.md --summary <text> --tags <a,b>
visionary draft publish --id <id> --confirm

相比原来的项目内脚本,全局 npm 包有几个明显优势:

  1. 不需要进入 Web 项目目录。
  2. 可以在写作目录、笔记目录、自动化脚本里直接调用。
  3. 命令形态更稳定,后续可以独立版本化、发布和迭代。
  4. 更适合被 Agent、CI、批处理脚本调用。
  5. 安装方式标准化,其他机器也可以通过 npm 快速使用。

项目结构

核心结构如下:

text
visionary-cli/
  package.json
  README.md
  LICENSE
  bin/
    visionary.mjs
  index.mjs
  api.mjs
  shared.mjs
  auth.mjs
  draft.mjs
  article.mjs
  column.mjs

这个结构有一个明显特点:入口很薄,业务按命令域拆分。

bin/visionary.mjs 只负责作为全局命令入口:

js
#!/usr/bin/env node

import { run } from '../index.mjs';

run();

真正的命令分发放在 index.mjs 中。它解析用户输入,然后把请求转交给对应模块:

js
if (command === 'auth') return handleAuthCommand(subcommand, options);
if (command === 'draft') return handleDraftCommand(subcommand, options);
if (command === 'article') return handleArticleCommand(subcommand, options);
if (command === 'column') return handleColumnCommand(subcommand, options);

这样设计的好处是:新增命令时不需要重写入口,只要新增一个模块,再在入口里注册一个分发分支即可。

npm 包设计

CLI 能像普通命令一样运行,关键在 package.json 的 bin 字段:

json
{
  "name": "visionary-cli",
  "version": "0.1.1",
  "type": "module",
  "bin": {
    "visionary": "bin/visionary.mjs",
    "visionary-cli": "bin/visionary.mjs"
  },
  "engines": {
    "node": ">=20"
  }
}

包名是 visionary-cli,全局安装方式是:

bash
npm install -g visionary-cli

安装后会暴露两个命令:

bash
visionary
visionary-cli

日常使用推荐 visionary,visionary-cli 作为别名保留。

本地开发阶段仍然可以使用 npm link:

bash
npm link

因为是软链接,后续修改源码后不需要重新安装,命令会直接使用当前目录下的最新代码。

设计原则一:CLI 只做客户端,不占用端口

Visionary CLI 不启动服务,不监听端口,也不常驻后台。

它的角色是一个 HTTP 客户端:用户执行命令后,CLI 读取参数和配置,向 Visionary 服务端 API 发请求,然后把结果以 JSON 输出。

默认服务地址是:

text
https://visionaryblog.cn

也可以通过参数或环境变量覆盖:

bash
visionary article list --base-url http://localhost:3000
VISIONARY_BASE_URL=http://localhost:3000 visionary article list

这意味着端口管理仍然属于 Web 服务本身,CLI 不需要处理端口冲突、进程守护、服务重启等问题。

设计原则二:配置分层,命令可复用

CLI 的配置读取顺序是:

  1. 命令行参数:--base-url、--token、--cookie
  2. 环境变量:VISIONARY_BASE_URL、VISIONARY_TOKEN、VISIONARY_COOKIE
  3. 本地保存配置:~/.visionary-cli/config.json
  4. 默认服务:https://visionaryblog.cn

这个顺序兼顾了几种场景:

  1. 临时调试时,用命令参数覆盖。
  2. 自动化脚本中,用环境变量注入敏感信息。
  3. 个人日常使用时,登录一次后复用本地配置。
  4. 没有任何配置时,默认访问线上服务。

登录命令会把服务地址和 token 保存到本地:

bash
visionary auth login --username <username> --password <password>

配置文件位置是:

text
~/.visionary-cli/config.json

保存时会创建 ~/.visionary-cli 目录,并用较保守的文件权限写入配置,避免 token 被无意暴露。

设计原则三:输出统一 JSON

Visionary CLI 的输出统一走 output():

js
export function output(data) {
  console.log(JSON.stringify(data, null, 2));
}

失败时也输出结构化 JSON:

js
export function fail(message, details) {
  output({ success: false, error: message, details });
  process.exitCode = 1;
}

这让 CLI 不只是给人看,也适合给脚本和 Agent 消费。例如创建草稿后,可以从返回结果里读取 draftId,再自动调用发布命令。

发布相关命令还会返回关键地址,例如:

json
{
  "draftUrl": "https://visionaryblog.cn/editor/draft/v2/666",
  "reviewUrl": "https://visionaryblog.cn/reader/review/500",
  "articleUrl": "https://visionaryblog.cn/reader/426"
}

对 AI 来说,这类结构化返回非常重要。它可以在发布后继续把草稿地址、审核预览地址和正式文章地址反馈给用户。

功能模块

当前 Visionary CLI 分为四个命令域。

auth:登录与本地凭证

auth 模块目前提供登录能力:

bash
visionary auth login --username <username> --password <password>

登录成功后会解析服务端返回的 set-cookie,提取 token,并写入本地配置。后续命令会自动把 token 转成 Cookie 请求头:

text
Cookie: token=<token>

draft:草稿生命周期

draft 是发布文章的核心模块,支持:

bash
visionary draft create
visionary draft get
visionary draft update
visionary draft publish

创建草稿时可以直接传入 Markdown 内容,也可以传入文件:

bash
visionary draft create --title "标题" --content-file ./post.md --summary "摘要" --tags "CLI,Visionary"

一个细节是,默认会去掉内容开头的一级标题:

md
# 标题

正文...

这样可以避免文章标题和正文里的 H1 重复。如果确实希望保留正文里的 H1,可以加:

bash
--keep-title

发布草稿时必须显式加 --confirm:

bash
visionary draft publish --id <id> --confirm

这是一个简单但有效的安全阀,避免脚本误操作导致文章被直接发布。

article:文章查询、删除与上传

article 模块覆盖已发布文章的管理能力:

bash
visionary article list
visionary article public-list
visionary article search --keyword <text>
visionary article get --id <id>
visionary article delete --id <id> --confirm
visionary article cover-upload --file <path>
visionary article image-upload --file <path>

删除文章同样要求 --confirm,这是命令行工具里值得保留的防误触设计。

上传能力基于 Node 20 的原生 FormData 和 Blob,不需要额外引入第三方依赖。

column:专栏管理

column 模块用于管理专栏:

bash
visionary column list
visionary column get --id <id>
visionary column create --name <name> --description <text>
visionary column update --id <id>
visionary column delete --id <id> --confirm
visionary column articles --id <id>
visionary column set-articles --id <id> --article-ids <id,id>
visionary column candidates
visionary column cover-upload --file <path>

其中 set-articles 支持逗号分隔和 JSON 数组两种输入方式,方便人工输入,也方便脚本生成。

为什么没有引入 Commander 或 Yargs

当前版本没有引入 Commander、Yargs 这类 CLI 框架,而是自己实现了一个轻量的 parseArgs()。

原因很直接:当前命令参数还比较简单,主要是 --key value 和少量布尔参数。手写解析器可以做到:

  1. 零运行时依赖。
  2. 可控的错误信息。
  3. 更容易保持 JSON 输出稳定。
  4. 包体积更小,调试路径更短。

这不是说永远不应该引入框架。当命令开始出现子命令嵌套、交互式输入、自动补全、复杂校验时,再引入成熟 CLI 框架会更合适。

当前阶段,最小实现反而更稳。

这个 CLI 适合什么场景

Visionary CLI 适合以下几类场景:

  1. 本地写 Markdown,然后一条命令创建草稿。
  2. Agent 自动生成文章后直接发布到 Visionary。
  3. 批量查询、搜索、删除文章。
  4. 管理专栏和专栏文章关系。
  5. 在 CI 或定时任务里做内容同步。
  6. 在不同机器上通过 npm 安装后快速复用同一套发布能力。

它本质上把 Web 后台里的内容操作抽象成了稳定命令。

后续可以继续演进的方向

当前版本已经完成了从“项目内脚本”到“独立 npm 包”的关键迁移。后续可以继续增强:

  1. 增加 auth status 和 auth logout。
  2. 增加 config get/set/list,让配置管理更显式。
  3. 增加 draft from-file 或 publish file,把创建草稿和发布串成一个命令。
  4. 增加 --format table,在人类阅读场景下输出更友好。
  5. 增加测试,覆盖参数解析、配置优先级和关键 API 请求构造。
  6. 增加 GitHub Actions,自动测试并发布 npm 包。

总结

Visionary CLI 的设计并不复杂,但它抓住了 CLI 工具最重要的几个点:

  1. 全局可用。
  2. npm 标准安装。
  3. 不常驻、不占端口。
  4. 配置分层清晰。
  5. 输出结构化。
  6. 命令域按业务拆分。
  7. 危险操作需要显式确认。

它不是一个“大而全”的命令行框架,而是一个围绕 Visionary 内容生产流程定制的工具。正因为边界清晰,它可以很自然地嵌入日常写作、自动化脚本和 Agent 工作流中。

从这个角度看,CLI 不只是一个发布入口,更是 Visionary 内容系统对外暴露的一层稳定操作协议。

评论
0/100