命令行与 agent
- 状态
- 生效中
- 更新
- 2026-09-15
- 适用范围
- ekko CLI
ekko 是 ZaunEkko 站内能力的命令行分身。你在网页上能看到什么,它就能读到什么——包括等级门禁后面的内容,因为它用的是你自己的身份。
它的设计目标不只是「给人用的命令行」,而是让别人的 agent 用起来和人访问网页一样顺畅。所以它有机器可读的能力清单、稳定的错误码,以及在管道里自动切换成 JSON 的输出。
安装
npm install -g @zaunekko/cli
包里是一个启动器加各平台已编译的二进制,装完就有 ekko 命令。
ekko version
不要从源码构建——那个仓库是私有的,那条路对你不通。
登录
ekko login
命令会打开浏览器,把你送到 ZaunEkko Account 的登录页;授权完成后浏览器会显示一行完成提示,终端同时拿到凭据。整个过程和你平时登录网站是同一个界面。
如果这台机器打不开浏览器(服务器、容器、SSH 会话),命令会自动改用验证码方式:终端打印一个网址和一串验证码,你在任意一台能上网的设备上打开那个网址、输入验证码即可。手机也行。
无论哪条路,终端始终会把网址打印出来——自动打开浏览器这件事在不少环境里会静默失败,只有打印出来才保证你还有办法继续。
ekko login --no-browser # 不尝试自动打开,只打印链接
ekko login --device # 直接走验证码,不尝试本机回调
ekko login --force # 已登录时也重新授权
登录一次能用很久:凭据会自动续期,只有连续很长时间不用才会失效。
你授出去的是什么
同意页会列出这次授权的权限。其中 blog.read 值得单独说一句:它让命令行读到你自己能读到的全部 Blog 内容,包括等级门禁后面的。这正是这个工具存在的意义——让 agent 看到和你在浏览器里看到的一样——但你应当知道自己授出去的是什么。
全部是只读。 发文、发说说、评论、上传都不在里面。
撤销
在 Account 的「登录与安全」页面,「已授权的应用」一栏可以随时撤销。撤销后该机器无法再续期;已经签发出去的访问令牌最长还能再用 15 分钟。
ekko logout 只清除本机凭据,不撤销服务端的授权——机器丢了要用上面那个入口。
能做什么
登录
ekko login 浏览器登录;起不了监听时自动改用验证码
ekko logout 清除本机凭据
ekko whoami 本机凭据里的身份与权限;不联网
ekko account 等级、积分与站点角色的当下值;只读
服务目录与调用
ekko catalog ls 列出公开服务
ekko catalog show <service> 服务详情;--version + --contract 看请求/响应契约
ekko invoke <service> 发起一次调用(会消耗积分)
ekko invocations ls 调用回执列表
ekko invocations show <id> 单条回执
ekko rate-limit 当前调用窗口额度
服务状态
ekko status 公开服务健康摘要
ekko status incidents 事件记录
ZaunEkko's Blog(只读)
ekko blog posts ls 你能读到的文章
ekko blog posts show <slug> 单篇文章
ekko blog moments ls 说说
给 agent
ekko schema 机器可读的能力清单
未登录时除 login / logout / version / help / schema 外一律不工作,不提供匿名只读降级。这是刻意的:能匿名跑一半的工具会让你分不清「我没登录」和「这个东西不存在」,而在 Blog 这种有等级门禁的地方,那个区别就是你看到的是全部还是半份。
上架服务、登记服务主体这类不可逆的操作不在命令行里,请在 Marketplace 控制台完成。
发起调用
ekko invoke pixiv.daily-ranking \
--version v1 \
--idempotency-key daily-ranking-2026-09-15 \
--input '{"mode":"daily"}'
--input 接三种形式:JSON 字面量、@文件路径、以及 @-(从标准输入读)。
--idempotency-key 是必填的,而且用错要花钱
它的作用是防止重复计费。网络超时、连接中断、重试——这些情况下你无法确定请求是否已经到达。此时用同一个 key 重发,平台会认出这是同一次调用,返回原来那次的结果,而不是产生第二笔消耗。
因此这个 key 必须:
- 对同一次业务意图稳定(重试时不变);
- 对不同业务意图唯一(换一次调用就换 key)。
一个可用的做法是把业务标识和时间窗拼进去,例如 daily-ranking-2026-09-15。
不要用随机值。 每次重试都换一个随机 key,等于把这个机制关掉——而关掉它的代价是重复扣费,且不会有任何报错。
契约的完整说明见认证与调用,那一页是调用面的权威。
两种凭据
ekko login | ZAUNEKKO_API_KEY | |
|---|---|---|
| 适合 | 你自己在终端里用 | 无人值守的定时任务 |
| 能做 | 全部命令 | 只能调用服务 |
| 读 Blog | 能 | 不能 |
| 有效期 | 自动续期 | 长期,可随时吊销 |
API key 在 Account 的「API key」页面创建。把它放进环境变量:
export ZAUNEKKO_API_KEY=zek_live_...
ekko invoke ... # 不需要登录
key 只能调用服务。治理类命令遇到它会明确报错,而不是让服务端回一个看不出原因的 403。
给 agent 的接入契约
输出形态由管道决定,不由参数决定
ekko catalog ls # 人在终端里敲 → 表格
ekko catalog ls | jq # 进管道 → JSON
ekko catalog ls > out.json # 重定向 → JSON
这与 ls、git 对颜色的处理是同一个惯例。--json 与 --table 显式覆盖这个判断。
表格是给人看的,格式随时可能调整,不要解析它。 JSON 转发服务端响应原样。
退出码
| 码 | 含义 |
|---|---|
0 | 成功 |
2 | 未登录——CI 里据此触发重新认证 |
1 | 其他失败 |
2 单独留给未登录,是为了让脚本不必去 grep 错误文案——文案会改,改一个字你的判断就静默失效了。
错误体
JSON 输出时,错误也写在 stdout(错误也是结果的一种,写进 stderr 会让调用方在 stdout 上读到空然后去猜):
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "...",
"hint": "调用没有发生,也没有扣费;等 retryAfter 秒后再提交新的调用",
"retryAfter": 37
}
}
code 是稳定的:NOT_LOGGED_IN、USAGE、NETWORK、UNAUTHORIZED、FORBIDDEN、NOT_FOUND、RATE_LIMIT_EXCEEDED、SERVER_ERROR、UNKNOWN。
按 code 判断,不要按 message 判断。
自我描述
ekko schema
输出全部命令、参数、哪些需要登录、哪些会消耗积分,以及输出形态与退出码的约定。agent 不必先读文档就能知道这个工具能做什么。
这份清单本身也是契约,字段刻意少而稳;schemaVersion 变了说明形状变了。
配置
全部通过环境变量,没有配置文件——装在别人机器上的工具,配置文件会变成排查时看不见的那一层。
| 变量 | 用途 |
|---|---|
ZAUNEKKO_API_KEY | 无人值守凭据 |
ZAUNEKKO_HOME | 凭据存放目录(默认 ~/.zaunekko) |
ZAUNEKKO_DEVICE_NAME | 「已授权的应用」里显示的机器名;设成空串则不上报 |
版本
ekko 有自己的版本号,与站点发布无关。它永不假设自己和服务端同版本:版本过旧只提示不阻断,发现新版本只打印提示,绝不自动安装。
升级和安装是同一条命令:
npm install -g @zaunekko/cli