# `qatlas` 客户端 CLI 参考 > **当前内容契约更新**:固定来源 PDF 已恢复为 paper_access 开关下的鉴权字节, > 关闭404,qa_、别名、Range、历史原件不能绕过。新内容在独立 qatlas-content 桶的 > content/ 命名空间不可变发布:source 绑定确切 PDF SHA,arXiv vN 是语义版本、 > S3VersionId 是物理版本、parse revision 是独立不可变身份;三者不可混用。 > 同 paper/SHA 可复用 source;冻结导入别名不同字节409(overwrite也不替换)。 > 解析上传必须完整支持的 Middle+Markdown,保留所有成员原名/字节,MD-only拒收; > manifest最后核验发布且PG必需,重传新revision,不覆盖历史。旧MD/JSON/images忽略, > 只在内容/PDF访问按需冻结旧PDF,不bulk回填、不删除旧桶;没有nightly/boot/入库自动推理。 > 下文涉及旧三桶覆盖、仅full.md+images、原PDF410/外部直链的段落保留为历史背景, > **不作为当前操作规则**。当前API/上传/续读统一见[论文内容契约](../server/paper-content.md) > 与[Upload API](../server/upload-api.md);完整read视图不是原Middle JSON。 `qatlas` 是独立 PyPI 包 [`qatlas-cli`](https://pypi.org/project/qatlas-cli/) 提供的 console script,由 [IAI-USTC-Quantum/qatlas-cli](https://github.com/IAI-USTC-Quantum/qatlas-cli) 独立维护和发版。本文保留与 QuantumAtlas 服务端集成相关的命令参考;客户端源码、完整命令和版本更新以该仓库及 `qatlas --help` 为准。 ```{admonition} 旧包 quantum-atlas 退役 :class: warning `quantum-atlas 0.21.0` 是仅含元数据的最终迁移版,不含 `qatlas` 模块、parser 库或 console entry,没有运行时依赖,也不会自动安装 `qatlas-cli`。安装 QuantumAtlas 主仓同样不会提供 `qatlas`。旧用户请按[迁移步骤](../getting-started.md#从旧包-quantum-atlas-迁移)先卸载旧包,再用原安装器安装 `qatlas-cli`;不要删除 `~/.config/qatlas`。 ``` ## 顶层 ``` qatlas [--version] [--help] [args...] ``` | 顶层选项 | 含义 | |---|---| | `--version` / `-V` | 打印 `qatlas X.Y.Z` | | `--help` / `-h` | 列出所有子命令 + 别名 | ## 通用 client flag 所有调 server 的子命令(paper / contrib 等)都接受这个 flag: | Flag | 默认 | 含义 | |---|---|---| | `--request-timeout ` | 120.0 | 单 HTTP 请求超时(per-call 临时调) | ~~~{admonition} v0.17.0 删除了 `--base-url` / `--token` / `--insecure` flag :class: note 服务器 URL / token / TLS 选项现在**只能**写在 `~/.config/qatlas/config.yaml`。短命令调用是 client 的主要使用场景,每次重新指 server 反而麻烦。如果有"两个项目走两个 server"的需要,用 `XDG_CONFIG_HOME=/path/to/other-config qatlas ...` 临时切(freedesktop 标准机制)。 ~~~ --- ## 客户端命令 ### `qatlas config` inspect / edit user-level YAML 配置文件(路径按平台,见下方表)。**首次跑任何 `qatlas ` 自动创建模板**——不再需要 `qatlas config init` 步骤。给 `uv tool install` 用户用,不需要 `export` 任何 env。 **跨平台配置文件路径**(由 [`platformdirs`](https://platformdirs.readthedocs.io/) 解析,跟主流 Python CLI 一致): | 平台 | 默认路径 | 控制 env | |---|---|---| | Linux | `~/.config/qatlas/config.yaml` | `XDG_CONFIG_HOME` | | macOS | `~/Library/Application Support/qatlas/config.yaml` | — | | Windows | `%APPDATA%\qatlas\config.yaml` | `APPDATA` | 不确定具体到哪 → `qatlas config path` 打给你。本文档后面的例子用 Linux 形式 `~/.config/qatlas/config.yaml`,mac / win 用户照着替换路径即可。 ``` qatlas config ``` | Subcommand | 含义 | |---|---| | `path` | 打印 yaml 文件路径(无论是否存在) | | `set ` | 写一个 key=value。`key` 用 **snake_case** YAML 字段名(如 `server_url` / `token` / `mineru_api_tokens`),不再用 env-var 大写形式。文件不存在时自动建(0600 perms)。敏感字段(含 `token` / `secret` / `key` / `password`)echo 时遮罩 | | `unset ` | 删除一个 key | | `get ` | 打印 key 在 yaml + Field default overlay 后的真值。无值时 exit 1,适合 shell 插值 | | `show [--unmask]` | dump 所有字段(snake_case key: value 形式);敏感值遮罩,`--unmask` 完整打印 | **配置入口**(v0.17.0+ 极简): 1. **平台原生配置文件路径**(见上表)— **唯一**配置源(首次跑任意命令自动创建) 2. **内置 Field default** — 由独立 [qatlas-cli 仓库](https://github.com/IAI-USTC-Quantum/qatlas-cli)中的客户端配置模型定义,不在 QuantumAtlas 主仓中 没有 CLI flag overrides,没有 OS env vars,没有 `$QATLAS_DOTENV` / `$QATLAS_CONFIG`。这是有意的极简化——client 用户基本是"配一次长期用"的模式,多入口反而增加心智负担(v0.16 起 client 不再借 server 的 `.env` 跑)。 ```{admonition} 想换 config 文件位置?用平台标准 env :class: note - **Linux**: `XDG_CONFIG_HOME=/etc/myconfig qatlas ` → yaml 落 `/etc/myconfig/qatlas/config.yaml` - **macOS**: 没有 platformdirs 支持的标准 env,唯一办法是 symlink `~/Library/Application Support/qatlas/` - **Windows**: `APPDATA=D:\my-config qatlas ` → yaml 落 `D:\my-config\qatlas\config.yaml` 都是 freedesktop / Microsoft / Apple 各自平台的原生机制(platformdirs 透传),不是 qatlas 自己定义的 override。 ``` ~~~{admonition} `qatlas config set` 会抹掉手写注释 :class: warning PyYAML 不保留 round-trip 注释,跟 `gh` / `kubectl config set` 行为一致。要永久注释,直接编辑 yaml 不用 `set`。auto-init 写出的 header 注释每次 `set` 都会被重写,是预期行为。 ~~~ **典型 workflow**: ```bash # 首次安装 + 配置(v0.17.0+) uv tool install qatlas-cli qatlas --help # 任意命令都触发 yaml 自动创建 qatlas config set server_url https://quantum-atlas.ai qatlas auth login -s quantum-atlas.ai # OAuth device-code flow → 自动写 hosts.yml echo '[jwt-a, jwt-b]' | qatlas config set mineru_api_tokens # 若要跑 qatlas contrib mineru(CSV / 列表均可;sensitive key 从 stdin 读) # 看 yaml 路径 qatlas config path # /home/you/.config/qatlas/config.yaml # 看效果 qatlas config show # server_url: https://quantum-atlas.ai # (token 不在 config.yaml;通过 `qatlas auth status` 查 hosts.yml) # ... # 之后任何 qatlas 子命令直接跑 qatlas paper get markdown 2501.00010v1 -o paper.md qatlas contrib mineru --batch-size 3 ``` > v0.16 / 更早升级到 v0.17.0:删了 `qatlas config init` 子命令、删了 `.env` → yaml 自动迁移。如果你之前在用 `~/.config/qatlas/.env`,**手工**把内容搬到 `~/.config/qatlas/config.yaml`(字段名从 `QATLAS_SERVER_URL` 改成 `server_url`、`MINERU_API_TOKEN` 改成 `mineru_api_tokens`(列表形式),去掉 `QATLAS_` 前缀小写化),原 .env 删掉。 详细:[管理凭证](manage-credentials.md)、[入门](../getting-started.md)。 --- ### `qatlas ingest`(已移除) `ingest` 子命令及其服务端端点(`POST /api/ingest/paper` 等)已随客户端 重构移除。原「让 server 抓论文并解析」的能力拆到了现役命令: | 原用法 | 现用法 | |---|---| | `qatlas ingest ` | `qatlas paper get markdown `(缓存未命中自动触发服务端抓取 + MinerU 转换,LRO 轮询) | | `qatlas ingest status ` | `qatlas paper status ` | | `--force-fetch` / `--force-parse` | 贡献者走 `qatlas contrib pdf --overwrite` / `qatlas contrib mineru` | --- ### `qatlas contrib` 贡献者日常入口(v0.19.0 起的正式命令面)。把"上传 PDF"和"本地跑 MinerU 再推回"两件事归到一个名词下: ``` qatlas contrib pdf --pdf [--overwrite] qatlas contrib mineru [] [--watch] [...] ``` #### `qatlas contrib pdf` 上传本地 PDF。需要 `papers:write` scope。 ``` qatlas contrib pdf --pdf [--overwrite] ``` | Flag | 必填 | 默认 | 含义 | |---|---|---|---| | `` | ✅ | — | **必含版本** `vN`,如 `2501.00010v1` | | `--pdf ` | ✅ | — | 本地 PDF 文件 | | `--overwrite` | ❌ | false | 字节不同时允许覆盖(旧版本保留在 S3 versioning 中)| 调用:`POST /api/papers/{arxiv_id}/upload-pdf`(multipart),自动加 `?expected_sha256=`。 详细 how-to:[贡献内容](contribute-content.md),详细 API:[Upload API](../server/upload-api.md)。 #### `qatlas contrib mineru` 本地用自己的 `MINERU_API_TOKENS` 跑 MinerU 解析,把结果(markdown + images)推回 server。三种模式: ``` qatlas contrib mineru # 队列模式:申请 lease 并处理 server 的 needs-mineru 队列 qatlas contrib mineru # 单篇模式 qatlas contrib mineru --watch [--watch-interval N] # 守护循环 ``` | Flag | 默认 | 含义 | |---|---|---| | `` | — | 省略 = 队列模式遍历 server 的未处理列表;给定(必含版本)= 只处理这一篇 | | `--batch-size N` | MinerU 单批上限 | 队列模式每批最多几篇 | | `--continue-on-error` | off | 队列模式遇到单篇失败继续(批模式下隐式开启)| | `--ttl-seconds N` | server 默认 1800(max 7200)| MinerU lease 租约时长 | | `--no-cache` | off | 让 MinerU 绕过 server 端缓存 | | `--overwrite` | off | 覆盖 server 上已有 markdown / images | | `--no-push` | off | 跑 MinerU 但不上传,结果 zip 留在临时目录、立即释放 lease | | `--watch` | off | 守护模式:每轮排空队列后 sleep `--watch-interval` 再轮询;隐含 `--continue-on-error`,SIGINT/SIGTERM 处理完当前篇干净退出 | | `--watch-interval N` | 300 | 守护模式两批之间的轮询间隔(秒)| 需要 `papers:write` scope。详细 how-to:[贡献内容](contribute-content.md) / [用 MinerU 解析](parse-with-mineru.md)。 --- ### `qatlas paper` 读 server 端缓存的 Markdown 字节与论文元数据。需要 `papers:read` scope。 资产读取(markdown / images)**仅对开启 `QATLAS_PAPER_ACCESS_ENABLED=true` 的 self-hosted 实例可用**——公开 `quantum-atlas.ai` 默认关,调用会被路由 404;`get metadata` 走论文详情端点 (`GET /api/papers/{id}`),不受该开关限制。 ``` qatlas paper get markdown ID_OR_DOI [--output FILE | --no-wait] qatlas paper get images ID_OR_DOI [--output FILE] # MinerU 图片 zip(显式获取) qatlas paper get metadata ID_OR_DOI # 论文详情 JSON(JSON-only、无 abstract,机器可读出口) qatlas paper status ID_OR_DOI [--kind markdown] # 当前固定原PDF用 qatlas paper pdf;可续读派生JSON用 qatlas paper read ``` #### ID 形态(server 自动归一) | 输入 | server 推断 | |---|---| | 完整 `0811.3171v3` / `quant-ph/9508027v2` | 不动 | | 无版本 `0811.3171` / `quant-ph/9508027` | 自动加 latest `vN`(优先查本地 catalog,未收录才 fetch `/abs/` HTML `og:url`)| | bare old-style `9508027` / `9508027v2` | 自动加 `category=quant-ph`(生产 bootstrap 假设;详见 [arxiv-ids §3.1](../reference/arxiv-ids.md))| | DOI `10.1103/PhysRevLett.103.150502` | OpenAlex 反查 → arxiv id | | `qa_` paper_id(`qa_01H…`)| 解析成该论文的 canonical 身份:最高已收录 arXiv 版本(DOI-only 论文走 DOI 管线)| 每应用一次默认值,CLI 在 stderr 打一行 `Note (server applied defaults): ...`, 来自 `X-QAtlas-Defaults-Applied` 响应头。`--quiet-notes` 抑制。 #### 长任务(LRO)行为 cache miss 时 server 返 202 + `Operation-Location` 启动后台 silent fetch / MinerU convert。CLI **自动 poll** `/{markdown|pdf}/status`,每轮打一行紧凑 进度到 stderr: ``` ... waiting: queued converting_md fetch=4503234B convert.running polls=7 queue=#3(2ahead,4/4slot) eta=240s ``` `--quiet-progress` 抑制;`--no-wait` 完全跳过 poll 直接吐 202 JSON 退出 0 (适合脚本异步编排)。 #### 错误展示 任何 4xx / 5xx 或 body-level 终态(`state=failed` / `cooldown` / `unavailable`), CLI 把 server JSON body 整段 pretty-print 到 stderr——包括 `detail` / `kind` (fatal | retryable | daily_limit)/ `phase` / `retry_after_iso` / `arxiv_id` | `doi` | `canonical` / 以及 echoed `requested_id` / `resolved_id` / `defaults_applied`。这样 agent 一次就拿到判断要不要重试、什么时候重试、原始 ID 解析到了哪一步所有信息。 #### Flag | Flag | 子命令 | 默认 | 含义 | |---|---|---|---| | `` | 全部 | 必填 | arxiv id、DOI 或 `qa_` paper_id(见上表)| | `--output / -o FILE` | get | stdout | 字节写到 FILE;`-` 或省略 = stdout | | `--no-wait` | get | false | cache miss 时不 poll,直接吐 202 JSON 退出 0 | | `--max-wait N` | get | 1800 | poll 总时长上限(秒);超时退出 1 | | `--quiet-progress` | get | false | 不打 `... waiting` 行 | | `--quiet-notes` | get / status | false | 不打 `Note (server applied defaults)` 行 | | `--kind markdown\|pdf` | status | markdown | 状态端点的两种 | #### 退出码 | 码 | 含义 | |---|---| | 0 | 字节成功 stream 完 / `--no-wait` 收到 202 / `status` 拿到 JSON | | 1 | terminal failure(404 / 5xx / `state=failed` / `cooldown` / 超时)| | 2 | flag 解析错误 | #### 调用链 - `get` 命中缓存:`GET /api/papers/{id_or_doi}/{kind}` → 200 + 字节 - `get` 未命中:同上 → 202 → 周期 `GET .../status` → `cached` → 重发 `GET .../{kind}` → 200 + 字节 - `status`:单次 `GET .../{kind}/status` 详细 LRO 协议、Phase 字段语义、agent 决策三元组见 [冻结内容访问 · 异步读取](../server/paper-content.md)。 --- ### `qatlas contrib mineru` 本地跑 MinerU 解析 server 上的 PDF,推回完整 MinerU bundle。需要 `papers:write` scope + 本地 `mineru_api_tokens` 至少一条。 ``` qatlas contrib mineru [arxiv_id] [options...] ``` | Flag | 必填 | 默认 | 含义 | |---|---|---|---| | `` (可选) | ❌ | — | 指定单篇;省略走队列模式 | | `--batch-size N` | ❌ | 50 | 队列模式:每批最多多少篇(硬上限 50 = MinerU 单批限制)| | `--max N` | ❌ | — | **已弃用**,`--batch-size` 的兼容别名;两个都给时 `--batch-size` 优先 | | `--continue-on-error` | ❌ | false | 队列模式:单篇失败继续下一篇(batch 模式下隐式启用)| | `--ttl-seconds N` | ❌ | server 默认 1800 | MinerU lease 租约(最长 7200)| | `--no-cache` | ❌ | false | 让 MinerU bypass 服务端缓存 | | `--overwrite` | ❌ | false | server 已有 markdown 时仍允许覆盖 | | `--no-push` | ❌ | false | 跑 MinerU 但不推回(留 tmp zip)| | `--watch` | ❌ | false | daemon 模式:跑完一批 sleep `--watch-interval` 再继续(Ctrl-C 干净退出;daily-limit 命中自动 sleep 到次日 00:01)| | `--watch-interval N` | ❌ | 300 | daemon 模式 sleep 秒数(不影响 daily-limit 命中后的睡眠时长)| **退出码**:成功 = 0;失败 = 1;MinerU 每日额度耗尽 = 75(`EX_TEMPFAIL`,CI 可视为 transient 重试)。 调用链(单篇模式):`POST /api/v1/papers/{id}/mineru-lease` → MinerU 单 task → `POST /api/papers/{id}/upload-mineru` → `DELETE /api/v1/papers/{id}/mineru-lease/{cid}`。 调用链(队列 / daemon 模式,v0.15.0+):list `needs-mineru` → 逐篇申请 MinerU lease → 一次 `POST /api/v4/extract/task/batch` → 周期 `GET /api/v4/extract-results/batch/{id}` → 每 done 立即 `upload-mineru` + 释放 lease。 详细:[用 MinerU 解析](parse-with-mineru.md)。 --- ### `qatlas auth` 管理本地存储的 PAT。 ``` qatlas auth login [-s | --server-url ] [--no-browser] [--scopes a,b,c] [--expires-days N] [--token-name NAME] [--timeout SEC] [--insecure] [--with-token] qatlas auth logout [-s | --server-url ] qatlas auth status [-s | --server-url ] qatlas auth token [-s | --server-url ] ``` | 子命令 | 行为 | |---|---| | `login` | 走 RFC 8628 device-code 流程:POST `/api/oauth/device/code` 拿 user_code + verification 深链 → 自动开浏览器(除非 `--no-browser`) → 用户在 `//device` 表单里编辑 name/scopes/expiry 然后 Approve → CLI 轮询 `/api/oauth/device/token` 拿到 PAT 明文写 hosts.yml。`--with-token` 是 CI 旁路 | | `logout` | 删该 host 的本地条目(不调 server)| | `status` | 列已登录的 host + 脱敏 token 预览 | | `token` | 把指定 host 的明文 token 打到 stdout(shell 替换:`curl -H "Authorization: Bearer $(qatlas auth token)"`)| | Flag | 默认 | 含义 | |---|---|---| | `-s` / `--server-url` | `server_url:` from config.yaml, else interactive prompt | 操作哪个 server;接受裸 hostname (`quantum-atlas.ai`) 或完整 URL (`https://quantum-atlas.ai:4200`),自动 normalize 成 hosts.yml key | | `--no-browser` | off | 不调 `webbrowser.open` —— 只打印 URL,自己复制到任意有浏览器的设备打开 | | `--scopes a,b` | 空 | **预填**到浏览器表单的 scope;空 = 浏览器默认全勾(用户可改)| | `--expires-days N` | `90` | 预填到表单的过期天数 (1–365);浏览器可改 | | `--token-name NAME` | `qatlas-cli--` | 预填到表单的 token 名;浏览器可改 | | `--timeout SEC` | `600` | CLI 等浏览器 approve 的秒数 | | `--insecure` | off | 信任自签证书(IP 入口 / 用 `tls internal` 的边缘节点)| | `--with-token` | off | CI 旁路:从 stdin 读 PAT(`cat token \| qatlas auth login -s ... --with-token`),跳过 OAuth 直接存进 hosts.yml;secret 不进 argv / shell history。跟 `gh auth login --with-token` 同款设计——故意不暴露 argv 形式 | 注意:所有非 CI 旁路的 flag 都只是**预填**,最终 token 的 name / scopes / expiry 以浏览器里点 Approve 时表单上的值为准——这设计就是为了让用户不必每次都精确记得自己想要哪几条 scope,先 `qatlas auth login` 跑起来再在浏览器里挑。 > v0.19.0 删了 `qatlas config set token` 路径——config.yaml 的 `token:` 字段会静默盖 hosts.yml 里所有 per-host token,是 footgun。所有 PAT 现在都通过 `qatlas auth login`(OAuth)或 `qatlas auth login --with-token`(CI stdin)走 hosts.yml。 文件 layout 是 YAML,0600 权限,详见 [管理凭据](manage-credentials.md)。 --- ## 插件命令(独立仓库提供) `qatlas` 的部分顶层命令由独立插件包提供(entry-point 组 `qatlas.plugins` 发现,安装插件包后命令自动出现,未安装时 CLI 会给出安装提示): | 命令 | 插件包 | 说明 | |---|---|---| | `qatlas search` | qatlas-search | 多源论文搜索(agentic 微服务,含本地语义检索 backend)| | `qatlas rag` | qatlas-rag | 直接查询 qatlas-rag 语义检索服务(`POST /v1/retrieve`),支持 `--server` / `--token` / `--max-results` / `--json` | | `qatlas match` | qatlas-match | 论文身份高精度匹配:判定 qatlas-id / DOI / arXiv / OpenAlex / URL / 标题是否已入库并返回统一 `qa_…` id(标题必须每个单词都匹配;`--author` / `--year` 消歧,`--json`、部署机 `--direct`)| 三个插件仓库均为私有仓(IAI-USTC-Quantum/qatlas-search、 IAI-USTC-Quantum/qatlas-rag、IAI-USTC-Quantum/qatlas-match),按提示中的 `uv tool install --from git+ssh://...` 方式安装。`qatlas rag` 需要能访问 qatlas-rag 服务(默认 `127.0.0.1:8801`,即部署该服务的主机或可达的内网 地址);`qatlas match` 默认经 qatlasd 的 `POST /api/papers/match` 代理调用 (需 `papers:read` scope 的 PAT),`--direct` 仅面向部署机就地连库。 ## 已退役的旧 Python 入口 QuantumAtlas 主仓不再分发原 `qatlas/` Python helpers,也不再提供旧 monolith 的 `qatlas parser` / `parse` 入口。`quantum-atlas 0.21.0` 不包含这些代码;不要通过安装主仓或旧包恢复它们。客户端可用命令以独立 `qatlas-cli` 的 `qatlas --help` 为准。 --- ## 环境变量影响 > v0.17.0+:**client 端不读任何 OS env**。所有 client 配置必须经 `~/.config/qatlas/config.yaml` 或 `~/.config/qatlas/hosts.yml` 持久化。 | 变量 | 谁读 | 作用 | |---|---|---| | `XDG_CONFIG_HOME` | 所有 `qatlas` 命令 | config.yaml + hosts.yml 父目录(默认 `~/.config`)| | `XDG_DATA_HOME` | 服务端 qatlasd | 默认 raw/data/pb_data 父目录 | | `QATLAS_*` env | **server 端 qatlasd**,参见 [qatlasd CLI](../server/cli-qatlasd.md) + [env vars](../reference/env-vars.md) | client 不读 | 完整列表:[环境变量参考](../reference/env-vars.md)。