Files
qinglong/cli/README.md
T
whyour 801a71d740 feat(cli): cover OpenAPI with a remote npm CLI and internal panel tools (#3074)
* feat(cli): add unified Commander CLI for QingLong 2.x

* fix(cli): publish via npm and address security review feedback

* ci(cli): package npm artifacts and remove evaluation collateral

* test(cli): use a fixed shell fixture for log retention

* refactor(cli): separate remote npm client from panel tools

* feat(cli): cover active panel OpenAPI resources

* docs(cli): unify authentication and skill guidance

* refactor(cli): isolate internal commands and generate Commander help

* refactor(cli): organize remote and internal modules by responsibility

* ci(cli): publish verified npm archives from master

* fix(cli): publish under the whyour npm scope

* ci: use npm trusted publishing for both packages

* docs: introduce the published CLI on the project homepage

* fix(cli): preserve server log truncation and correct login hints

* fix(cli): accept dashboard record request bodies

* fix(cli): preserve stdin for local task execution

* fix(cli): resolve task executables after changing directory

* fix(cli): preserve shell function tasks and sanitize test failures

* fix(cli): preserve shell hook state and resolve workdir after hooks

* fix(cli): preserve cleanup across shared shell task timeouts

* fix(cli): isolate shell control descriptors and reap timed-out descendants
2026-09-25 23:24:41 +08:00

12 KiB
Raw Permalink Blame History

QingLong 2.x 远程管理 CLI

简体中文 | English

npm 与面板内部入口都叫 ql,但使用独立的 Commander 命令树:npm 入口只调用远程 API,内部入口只运行本机工具。使用前确认可执行文件的绝对路径和 --help;安装 npm 包不会迁移内置 Shell 命令。

@whyour/qinglong-cli 是独立 npm 包,覆盖当前 develop 的有效 OpenAPI,只注册一个 ql 命令。它不包含脚本执行器或本机运维实现;task exec、repo/raw、reload/update/reset* 等属于面板内部工具,不随 npm 包分发。开发发布也不属于 CLI 范围。

安装与使用

要求 Node >=22.12,推荐 Node 24。Commander 在构建时打包,无额外运行时 npm 依赖,不单独发布 CLI 镜像。

npm install -g @whyour/qinglong-cli
ql --help
# 临时使用,不覆盖面板已有的 ql
npm exec --package=@whyour/qinglong-cli -- ql --help

也可以从源码构建:执行 npm ci --prefix cli、npm run build:cli,在 cli 目录执行 npm pack,然后安装本地 tgz。全局安装会占用 ql 名称;已有面板的机器建议使用独立 npm prefix 或直接执行 node /absolute/path/to/cli/dist/npm/ql.js。

ql login --url https://ql.example.com
ql auth status --json
ql task list --search 示例 --page 1 --size 50 --json
ql task get 12 --json
ql task logs 12 --tail 200 --json
ql task run 12 --json
ql task stop 12 --json
ql auth status --scope subscriptions --json
ql subscription list --json
ql subscription get 5 --json
ql subscription run 5 --json
ql subscription stop 5 --json
ql subscription logs 5 --tail 200 --json
ql subscription enable 5 --json
ql subscription disable 5 --json
ql auth logout --json

各命令支持 --help,QL_LANG=en 切换英文帮助;命令和 JSON 字段不随语言变化。ql task 中只包含 API 操作,不会把无效命令解释为本机脚本,也不提供独立 task npm 入口。

全量 OpenAPI 管理

现在还支持任务/订阅创建、修改、删除,应用管理与密钥重置,以及环境变量、配置、脚本、日志、依赖、系统、仪表盘和用户管理。ql api routes --json 列出全部 143 条有效路由;3 条已下线文件读取接口不包含在内。新增命令在 完整双语参考 中逐项列出,路由覆盖由 CI 与后端代码核对。

ql task create --name demo --command 'task demo.js' --schedule '0 0 * * *' --json
ql subscription create --type public-repo --url https://example.com/repo.git --alias demo --schedule-type crontab --schedule '0 0 * * *' --json
ql app create --name agent --scopes crons,subscriptions --show-secrets --json
ql env create --data @envs.json --json
ql api request PUT /open/crons/run --data '[12,13]' --json

请求体使用 --data JSON/@file/-,查询使用 --query,上传 --file,下载 --output。新命令支持 --timeout 秒数;旧命令行为保留,完整参数可用 api request。下载不覆盖现有文件,应用密钥默认隐藏,明确加 --show-secrets 才输出。新增资源通常保留原始返回字段,注意环境、配置和会话信息可能敏感。

远程 ql system ... 调用面板 API;本机 reload/reset 等仍不在 npm 包中。不要将远程 API 覆盖理解为本机运维重新混入包。

认证

受保护的请求均使用 Authorization: Bearer <token>,支持以下两种凭据来源:

方式 提供方式 保存与刷新
应用凭据登录 ql login --url <面板地址>,输入 Client ID / Client Secret;自动化通过 QL_CLIENT_ID、QL_CLIENT_SECRET 注入 通过 /open/auth/token 换取 token,凭据和 token 保存到本机,过期自动刷新
直接访问令牌 环境中同时设置 QL_URL、QL_ACCESS_TOKEN,支持有效应用 token 或面板会话 token 优先于保存的配置,不落盘、不自动刷新

应用凭据登录

在面板创建专用应用,按需授予 crons、subscriptions、envs 等权限。login 与 auth login 等价;交互输入 Client ID 和不回显的 Client Secret,密钥不支持命令行参数。

ql login --url https://ql.example.com
ql auth status --scope crons --json
# 自动化环境事先注入 QL_CLIENT_ID 和 QL_CLIENT_SECRET,再运行同一 login 命令

凭据和 token 保存到 ~/.config/qinglong/cli.json,为当前用户所有的 0600 明文文件。QL_CLI_CONFIG 可选择其他配置文件。登录只验证应用凭据,不意味着具备所有资源权限。

直接使用访问令牌

在终端或 CI 的凭据配置中注入 QL_URL 与 QL_ACCESS_TOKEN 后,直接调用命令,无需再执行 login:

ql auth status --scope apps --json
ql app list --json

两个变量必须同时提供给受保护命令;只提供一个会报参数错误。此方式优先于 QL_CLI_CONFIG 指定的配置。即使重新执行 login 写入了应用配置,后续请求仍使用环境令牌;切回保存的应用配置时,在自己的终端执行 unset QL_URL QL_ACCESS_TOKEN。

应用管理需要 apps 权限或有效的授权面板会话;当前面板 UI 没有列出所有后端 scope,CLI 不自动提权。匿名 user login 可在仅设置 QL_URL 时使用 --data @credentials.json 或 --data - 提交凭据;返回的会话不会自动保存。启用双因素认证时,服务端 420 对应 CLI 退出码 3,随后使用 user two-factor-login,请求体包含 username/password/code。不要将凭据写入命令参数或聊天。

权限检查、退出与失败

auth status 默认检查 crons 读取权限,可用 --scope subscriptions、--scope apps 等检查对应资源。它只验证代表性读取,不代表所有写操作都获授权。2.x 的 crons scope 同时覆盖读取和执行。

auth logout 仅删除本机应用配置,不撤销服务端 token,也不清除父进程中的 QL_ACCESS_TOKEN。令牌模式仍可能继续访问;如需撤销访问,使用面板提供的应用或会话管理。重置应用密钥会使旧应用 token 失效,需要使用新密钥重新 login。

URL 使用面板根地址,可包含代理路径前缀,不附加 /open。远程连接要求 HTTPS,回环地址允许 HTTP;不跟随重定向。2.x 应用 token 接口以查询参数传递凭据,代理应避免记录该查询字符串。401/403 不自动重放操作;直接令牌失效时需更换,CLI 不会转而使用保存的应用凭据。

输出契约

订阅支持 create/update/delete/list/get/run/stop/logs/enable/disable/status/log-files。subscription list/get 保留管理字段投影;run/stop/enable/disable 返回 subscriptionId/action/accepted。创建、修改、删除及通用 API 请求保留服务端响应,可能包含敏感字段;不能将所有变更都解释为 accepted。

命令默认输出缩进 JSON,--json 输出单行 JSON;成功写 stdout,错误写 stderr,互不混用。帮助在 --json 下也使用 {code:200,data:{help:"..."}}。

  • task list:{code:200,data:{data:[...tasks],total:123}},默认每页 50,最多 200;任务字段保留服务端内容。
  • task get:{code:200,data:{id:12,...}}。
  • task logs:{code:200,data:"日志尾部",logStatus:"completed",truncated:true}。默认 200 行,最多 10000;旧接口若不返回 logStatus,该字段省略。
  • task run/stop:{code:200,data:{taskId:12,action:"run",accepted:true}}。仅表示请求被接受,不表示任务成功完成。
  • 错误:{code:1,message:"..."};退出码 1 为 API/网络/配置错误,2 为参数错误,3 为未登录、HTTP/API 401/403 或需要双因素验证;成功退出码 0。

ID 必须为正整数,运行/停止一次操作一个任务。2.x 没有为此提供独立运行 ID 或幂等键,失败响应可能意味着执行结果未知,应先查询状态,禁止盲目重试。CLI 不自动重试 HTTP 请求。

日志是该任务最新日志,可能属于先前运行;completed 不代表成功。--tail 在客户端截取,不减少服务端读取量或网络传输量。日志与任务字段不自动脱敏,应按需读取并避免向聊天中暴露敏感信息。

Skill、构建与验证

npm 包附带 skills/qinglong-cli,覆盖全部远程命令。复制到所用 Agent 的 skills 目录,并确认使用 npm 远程入口。Skill 不存放凭据,也不替代服务端权限。

源码使用 TypeScript 严格检查和 Commander 解析;共用参数/输出逻辑,分别构建远程入口和面板内部入口。dist/npm/ql.js 是可独立运行的远程 bundle,构建依赖图会拒绝引入本机运维模块;npm 文件白名单仅包含该 bundle、source map、许可证、中英文说明和远程 Skill。完整 dist 用于面板内部构建,不随 npm 发布。

npm ci --prefix cli
npm run check:cli
npm run test:cli
node cli/scripts/verify-package.cjs

测试覆盖请求格式、认证刷新、输出、错误、禁止自动重试、权限和独立安装。打包验证会离线安装 tgz,并确认唯一入口是 ql,本机命令不可调用。

CLI package 工作流在相关 PR、develop/master 推送和手动触发时执行 Node 22.12/24 类型检查、构建及测试。Node 24 上传通过离线安装验证的 qinglong-cli-<commit> artifact。两个矩阵任务成功后,master 推送会将这份已验证的 tgz 发布到 npm 的 latest 标签,通过 GitHub Actions OIDC 可信发布,无需 NPM_TOKEN Secret。手动运行需选择 master 并勾选 publish;PR、develop 和 fork 不发布。npm 的 Trusted Publisher 需绑定仓库 whyour/qinglong 和工作流 cli-package.yml,允许 npm publish;面板包 @whyour/qinglong 单独绑定 build-docker-image.yml。发布 job 使用 Node 24,并仅在该 job 授予 id-token: write。新包需先完成首次发布,再配置对应包的可信发布关系。

CLI 版本独立维护在 cli/package.json 和 cli/package-lock.json。发布改动前执行 npm version patch --prefix cli --no-git-tag-version(也可用 minor/major),提交这两个文件。已发布的版本会提示并跳过;registry 查询失败则停止发布。该流程仅发布 X.Y.Z 稳定版本,不重新构建产物或执行包生命周期脚本。

面板内部工具

本机执行、订阅同步和运维随面板源码/构建交付,使用完整内部 dist 与独立 qinglong-local Skill;源码说明见 cli/LOCAL.md 和 cli/LOCAL.en.md。npm 包不能用作 QL_CLI_ROOT。账号恢复、服务重载必须在实际面板宿主机或容器中执行;Docker 使用 docker exec 调用容器内选定入口。

API task run 返回请求接受,本机执行器等待脚本结束,二者耗时不能直接对比。Shell 迁移性能应比较相同配置和脚本下的内部 TS 执行器与原 Shell;远程 API 操作没有对应的旧 Shell 管理命令。

源码结构

src/
  entrypoints/     # 可执行入口组装
  remote/          # commands / api / auth
  internal/        # commands / execution / subscription / maintenance / runtime / integration
  compatibility/   # 旧入口和参数适配
  shared/          # cli / i18n / 响应类型与错误

shared 不能引用业务模块;remote 与 internal 只能引用各自区域及 shared,集成测试检查这些依赖边界。两套业务注册表分别注入共享 Commander 解析器。

测试按同样的区域分组。仓库根目录 npm run test:cli 或 cli/ 下 npm test 自动发现标准测试;test/linux 保持为显式执行的容器集成测试。scripts/entrypoints.cjs 保留现有 dist 可执行入口,以及 dist/local/entrypoints.js、cronEntrypoint.js;移动源码不要求修改 QL_CLI_ROOT 或 cron 命令。