# BlueArchiveToolkit 用户指南 本指南面向 `bat` 官方资源同步二进制的使用者,覆盖:命令、选项、退出码、运行时行为,以及统一错误码参考。 - 权威运行/部署说明另见 `docs/guides/official-resource-test-pull.md` 和 `docs/guides/deployment.md`。 - 本文档中的命令、选项以 `bat --help` 为准;错误码以 `core/src/error_code.rs` 的码表为准。 --- ## 1. 概览 `bat` 是 Linux 上官方日服(Yostar JP)资源同步的正式入口。它可以: - `--auto-discover` 从官方 HTTP metadata 解析 `GameMainConfig`,自动获得 app-version、连接组和 server-info,不安装、不启动官方启动器。 - 生成官方全量 pull plan、执行真实下载,维护 release 内的下载 manifest,并做 size + BLAKE3 复用校验、官方 seed `.hash`(标准 xxHash32(seed=0))强校验、ZIP 结构校验。 - 断点续传、失败分类重试、下载 quarantine、本地 manifest audit/repair。 - 原子发布:先写 `.staging/`,校验通过后发布 `versions/` 并原子切换 `current` symlink。 - 常驻运行(`--watch`)或后台化(`--daemon`),通过 `bat.sock` Unix socket JSON-RPC 控制。 ### 运行形态 ```bash # 一次性 dry-run(不写状态) bat --auto-discover --dry-run # 前台常驻 bat --auto-discover --watch --output /var/lib/bluearchive-toolkit/official # 后台守护 bat --auto-discover --daemon --output /var/lib/bluearchive-toolkit/official ``` 带凭据的代理**推荐用环境变量**(凭据不进命令行/argv/状态文件): ```bash HTTPS_PROXY=http://user:pass@127.0.0.1:7890 bat --auto-discover --daemon ``` --- ## 2. 命令 无子命令时执行一次性同步(或配合 `--watch`/`--daemon`)。子命令如下: | 命令 | 说明 | |---|---| | `refresh` | 执行一次更新检查;若有 live daemon,则通过 RPC 请求其刷新 | | `verify` | 校验远端计划、本地 manifest 和官方 seed hash(dry-run + 审计当前 release) | | `repair` | 重新下载本地校验失败的资源 | | `status` | 显示 daemon 状态 | | `stop` | 停止 daemon | | `restart` | 重启 daemon;未显式传参时复用保存的参数 | | `reload` | 请求 daemon 重新自动发现并强制刷新 | | `logs` | 显示 daemon 日志尾部(配合 `--tail`) | | `doctor` | 运行时诊断(输出目录、状态目录、curl/unzip、代理、PID、socket、锁) | | `clean-stable` | 清理 `.part`/`.tmp`/失效锁、PID、socket(daemon 运行中会拒绝执行) | `status`/`stop`/`logs`/`reload` 和默认形态的 `refresh` 优先走 `bat.sock` JSON-RPC;socket 不可用时 `status`/`stop` 回退到 PID/状态文件兼容路径。 --- ## 3. 选项 ### 发现(Discovery) | 选项 | 说明 | |---|---| | `--auto-discover` | 自动发现 app-version、server-info、连接组 | | `--server-info-url ` | 使用官方 server-info URL | | `--server-info-file ` | 使用官方 server-info 文件名 | | `--server-info-path ` | 使用本地 server-info JSON 文件 | | `--app-version ` | 覆盖 app 版本 | | `--connection-group ` | 覆盖连接组 | | `--launcher-version ` | 启动器 metadata API 版本(默认 `1.7.2`) | ### 同步(Sync) | 选项 | 说明 | |---|---| | `--platforms ` | 平台,如 `Windows,Android`(默认 `Windows,Android`) | | `--output ` | 资源发布根目录(默认 `./bat-resources`) | | `--snapshot ` | 覆盖 snapshot 路径(默认 `/current/official-sync-snapshot.json`) | | `--curl ` | curl 可执行文件(默认 `curl`) | | `--proxy ` | curl 代理覆盖(默认 `auto`,从环境变量检测)。scheme 支持 http/https/socks4/socks4a/socks5/socks5h | | `--no-proxy` | 强制直连 | | `--unzip ` | unzip 可执行文件(默认 `unzip`) | | `--dry-run` | 不写同步状态 | | `--plan` | dry-run 时输出计划中的 URL | | `--force` | 强制下载/刷新 | | `--audit-local` / `--no-audit-local` | 启用/关闭本地 manifest 审计 | | `--repair` / `--no-repair` | 启用/关闭自动修复 | ### 守护(Daemon) | 选项 | 说明 | |---|---| | `--watch` | 前台常驻循环 | | `--daemon` | 启动脱离终端的后台 watch 进程 | | `--state-dir ` | 后台状态目录(默认 `/tmp/bat-pid`) | | `--interval ` | 正常检查周期(默认 `1h`) | | `--error-retry ` | 失败后重试周期(默认 `60s`) | | `--quiet-up-to-date` / `--no-quiet-up-to-date` | 静默/总是打印 up-to-date 报告 | | `--tail ` | `logs` 命令返回的日志行数(默认 `200`) | ### 输出(Output) | 选项 | 说明 | |---|---| | `--human` | 人类可读输出(默认) | | `--json` | 面向脚本的稳定 JSON 输出 | | `--progress` / `--no-progress` | 启用/关闭 stderr 进度日志 | | `--banner` / `--no-banner` | 启用/关闭启动横幅 | | `-h`, `--help` | 显示帮助 | ### 默认值与运行时行为 - 平台:`Windows,Android`。 - 资源输出:`./bat-resources`(`current` → `versions/`、`.staging/`)。 - 后台状态目录:`/tmp/bat-pid`(`bat.sock`、`bat.pid`、`bat-status.json`、`bat-daemon.log`、`bat-events.jsonl`、短生命周期 `bat-control.lock`;代理凭据在 `bat-proxy.secret`,`0600`)。 - 强制刷新:每天北京时间(UTC+8)`03:00`、`16:00`、`18:00` 各一次。 - 状态类文件默认 `0600` 权限,读写不跟随 symlink。 --- ## 4. 退出码 | 退出码 | 含义 | |---|---| | `0` | 成功;`verify`/`doctor` 检查通过 | | `1` | 普通错误 | | `75` | 资源目录锁冲突(有 live daemon 正在管理同一目录,或 `.official-sync.lock` 被占用) | `verify` 或 `doctor` 发现问题也返回非 0。`--json` 模式下错误以 JSON 写到 stderr。 --- ## 5. 错误码参考 `bat` 使用一套稳定的数字错误码,作为 CLI、daemon RPC 和结构化日志的公共错误契约。 - **格式**:`BAT-ERR-<域 3 位><序号 3 位>`,共 6 位,例如 `BAT-ERR-300012`。 - **域**:错误码首位按百位分大类。 - **承载结构**(RPC envelope 的 `error` 字段、`--json` 输出、`bat-events.jsonl` 一致): ```json "error": { "code": "BAT-ERR-310001", "kind": "proxy_failure", "domain": "network", "location": "official-sync.download.pull_one", "message": "代理返回 407 认证失败", "retryable": false } ``` `location` 是稳定的「组件·操作」标签(跟随语义、不随行号漂移)。`retryable` 是该类错误的默认可重试性。 > 说明:错误码模型(`core/src/error_code.rs`)已建立并作为公共契约;将各链路的报错逐步接入到该码表的工作在 issue #1 下推进。下表随码表更新。 ### 域一览 | 域 | 区间 | 含义 | |---|---|---| | input | `1xxxxx` | 输入/配置:CLI 参数、配置校验、代理配置 | | path_security | `2xxxxx` | 路径/安全边界:危险目录、路径逃逸、symlink、权限 | | network | `3xxxxx` | 网络/下载:HTTP/网络错误、代理故障、重试耗尽、quarantine | | integrity | `4xxxxx` | 校验/完整性:BLAKE3、官方 seed hash、size、ZIP 结构 | | publish_storage | `5xxxxx` | 发布/版本状态/存储:staging、原子发布、version-state、锁、CAS | | parse | `6xxxxx` | 解析/适配:manifest、UnityFS、GameMainConfig | | task_rpc | `7xxxxx` | 任务/RPC:未知方法、参数非法、未实现、任务不存在 | | internal | `9xxxxx` | 内部/未知:兜底 | ### 码表 | 码 | kind | 可重试 | 含义 | |---|---|:---:|---| | `BAT-ERR-100001` | missing_app_version | 否 | 缺少应用版本(未传 `--app-version` 且未启用 `--auto-discover`) | | `BAT-ERR-100002` | missing_connection_group | 否 | 缺少连接组 | | `BAT-ERR-100003` | missing_server_info_source | 否 | 缺少服务器信息来源 | | `BAT-ERR-100004` | invalid_proxy_scheme | 否 | 代理 URL scheme 不受支持 | | `BAT-ERR-100010` | invalid_argument | 否 | 命令行参数无效 | | `BAT-ERR-200001` | dangerous_output_root | 否 | 输出目录被判定为危险路径 | | `BAT-ERR-200002` | path_escape | 否 | 路径逃逸出允许根目录 | | `BAT-ERR-200003` | symlink_rejected | 否 | 目标不允许是 symlink 或路径组件含 symlink | | `BAT-ERR-200004` | file_permission | 否 | 文件权限或模式错误 | | `BAT-ERR-300001` | http_forbidden | 否 | HTTP 403 | | `BAT-ERR-300002` | http_not_found | 否 | HTTP 404 | | `BAT-ERR-300003` | http_client_error | 否 | HTTP 其它 4xx | | `BAT-ERR-300004` | http_too_many_requests | 是 | HTTP 429 / 请求过多 | | `BAT-ERR-300005` | http_server_error | 是 | HTTP 5xx | | `BAT-ERR-300010` | network_dns | 是 | DNS 解析失败 | | `BAT-ERR-300011` | network_connect | 是 | 连接失败 | | `BAT-ERR-300012` | network_timeout | 是 | 超时 | | `BAT-ERR-300013` | network_tls | 是 | TLS 失败 | | `BAT-ERR-300014` | network_interrupted | 是 | 传输中断 | | `BAT-ERR-300015` | network_other | 是 | 其它网络错误 | | `BAT-ERR-300020` | retry_exhausted | 否 | 下载重试次数耗尽 | | `BAT-ERR-300021` | quarantined | 否 | URL 因反复失败进入 quarantine | | `BAT-ERR-300030` | non_official_url | 否 | URL 不是官方 host(被拒绝) | | `BAT-ERR-300031` | launcher_api_rejected | 否 | 官方启动器 API 返回非 200 业务码(版本/鉴权被拒等) | | `BAT-ERR-310001` | proxy_failure | 否 | 代理自身故障(认证/解析/连接代理失败) | | `BAT-ERR-400001` | blake3_mismatch | 否 | 本地 BLAKE3 与 manifest 不符 | | `BAT-ERR-400002` | official_hash_mismatch | 否 | 官方 seed `.hash`(xxHash32)校验不符 | | `BAT-ERR-400003` | size_mismatch | 否 | 文件大小与 manifest 不符 | | `BAT-ERR-400004` | zip_structure_invalid | 否 | ZIP 结构无效 | | `BAT-ERR-500001` | resource_locked | 否 | 资源目录锁冲突(对应退出码 75) | | `BAT-ERR-500002` | staging_prepare_failed | 否 | staging 准备失败 | | `BAT-ERR-500003` | publish_failed | 否 | 原子发布失败 | | `BAT-ERR-500004` | version_state_write_failed | 是 | 版本状态写入失败 | | `BAT-ERR-500010` | cas_hash_mismatch | 否 | CAS 对象 Hash 不匹配 | | `BAT-ERR-500011` | cas_object_not_found | 否 | CAS 对象不存在 | | `BAT-ERR-500012` | cas_reference_underflow | 否 | CAS 引用计数下溢 | | `BAT-ERR-500013` | cas_database | 否 | CAS 元数据库错误 | | `BAT-ERR-600001` | manifest_parse_failed | 否 | Manifest / Addressables catalog 解析失败 | | `BAT-ERR-600002` | unityfs_parse_failed | 否 | UnityFS 解析失败 | | `BAT-ERR-600003` | game_main_config_failed | 否 | GameMainConfig 解密/解析失败 | | `BAT-ERR-600004` | launcher_response_invalid | 否 | 官方启动器链内容无效(API 响应、远端 manifest 或包内容无法解析/缺少必需内容) | | `BAT-ERR-700001` | rpc_unknown_method | 否 | 未知 RPC 方法 | | `BAT-ERR-700002` | rpc_invalid_params | 否 | RPC 参数无效 | | `BAT-ERR-700003` | rpc_not_implemented | 否 | 方法/命名空间尚未实现 | | `BAT-ERR-700004` | task_not_found | 否 | 任务不存在 | | `BAT-ERR-900001` | internal | 否 | 未归类的内部错误 | 新增错误码在 `core/src/error_code.rs` 的码表登记后,同步更新本表。 --- ## 6. Daemon RPC 接口 `bat --daemon` 在后台状态目录下创建 `bat.sock`(Unix socket),提供**换行分隔的 JSON-RPC 2.0** 控制面。CLI 的 `status`/`stop`/`logs`/`reload`/`refresh` 优先走它;Go 服务层也应通过这个进程边界调用,而非 FFI。 ### 传输与 envelope - 请求:一行 JSON-RPC 2.0,`{"jsonrpc":"2.0","id":,"method":".","params":{…}}`。 - 响应:JSON-RPC 2.0,应用层负载统一装入 `result` 的 envelope: ```json {"jsonrpc":"2.0","id":1,"result":{ "ok": true, "status": "ok", "data": { … }, "request_id": "req--" }} ``` - `ok`:应用层成功与否。失败时 `ok=false`、`status="error"`、`error` 为 §5 的 `ApiError` 结构(含 `BAT-ERR` 码)。 - `status`:`ok` | `accepted`(长任务已入队) | `error`。 - `request_id`:进程内唯一请求 ID。 - 请求解析失败(畸形 JSON)走 JSON-RPC 顶层 `error`(如 `-32700`),不进 envelope。 ### 方法命名空间 方法采用 `.`。`bat.*` 为兼容别名(解析为 `daemon.*`)。 | 方法 | 状态 | 说明 | |---|---|---| | `daemon.status` | ✅ | 后台状态快照 | | `daemon.logs` | ✅ | 日志尾部(`params.tail`,默认 200) | | `daemon.stop` | ✅ | 请求停止(`accepted`) | | `daemon.reload` | ✅ | 请求重新发现并强制刷新(`accepted`) | | `daemon.refresh` | ✅ | 请求刷新检查(`params.force`,`accepted`) | | `resource.state` | ✅ | 资源发布根 + 版本状态 + 上次同步结果 | | `resource.sync` | ✅ | 触发同步任务(`params.force`),返回 `task_id` | | `resource.verify` | ✅ | 触发校验任务(dry-run + audit),返回 `task_id` | | `resource.manifest` | ✅ | 当前版本下载 manifest 分页查询(`params.offset` 默认 0、`params.limit` 默认 100/上限 1000) | | `catalog.status` | ✅ | 当前已发布版本的 catalog 概览(app/bundle 版本、addressables 根、端点与 marker 计数、launcher 元数据) | | `catalog.versions` | ✅ | 版本历史:current / in_progress / previous / failed | | `catalog.diff` | ✅ | 当前 snapshot 相对上一个可用版本的差异(base_delta + extended_delta + 变更端点 URL) | | `catalog.refresh` | ✅ | 触发 catalog 更新检查任务(dry-run 计划,不下载;`params.force`),返回 `task_id` | | `task.status` | ✅ | 查询任务(`params.task_id`) | | `task.list` | ✅ | 列出全部任务(最新在前) | | `task.cancel` | ✅ | 请求取消任务(`params.task_id`);协作式,在同步检查点生效 | | `task.logs` | ✅ | 返回任务的进度日志(`params.task_id`,有界) | | `resource.repair` / `patch.*` / `unityfs.*` / `task.create` | ⏳ | 已规划,返回 `BAT-ERR-700003`(not implemented);repair 待引擎支持独立修复模式,patch/unityfs 待引擎实现 | | 未知方法 | — | `BAT-ERR-700001`(unknown method) | 只读查询(`resource.state` / `resource.manifest` / `catalog.status` / `catalog.versions` / `catalog.diff`)在尚无已发布版本或对应文件不存在时返回 `ok: true` 且 `data.available: false`(正常状态而非错误,便于调用方直接分支)。 ### 任务模型 `resource.sync` / `resource.verify` / `catalog.refresh` 是**异步任务**:入队即返回 `{ "task_id": "task--", "kind": "resource.sync" }`(`status: "accepted"`),实际执行由后台任务 worker 串行完成,通过 `task.status` / `task.list` 轮询。任务记录: ```json { "id": "task-1234-1", "kind": "resource.sync", "status": "queued|running|succeeded|failed", "stage": "download", "message": "…", "created_at": …, "updated_at": …, "started_at": …, "finished_at": …, "error": { … }, "result": { … } } ``` - `task.cancel` 请求取消:置任务的取消标志,worker 在下一个同步检查点中止,任务转为 `cancelled`(协作式,不硬杀正在执行的 curl)。 - 与后台 watch 循环的定时同步通过进程内锁互斥(任务等待而非失败)。 - 任务目前**仅存内存**,随 daemon 生死;持久化(重启后仍可查)为后续工作。 ### 示例 ```bash # 触发同步任务并取回 task_id(socat 演示) printf '{"jsonrpc":"2.0","id":1,"method":"resource.sync","params":{"force":true}}\n' \ | socat - UNIX-CONNECT:/tmp/bat-pid/bat.sock # 轮询任务 printf '{"jsonrpc":"2.0","id":2,"method":"task.status","params":{"task_id":"task-1234-1"}}\n' \ | socat - UNIX-CONNECT:/tmp/bat-pid/bat.sock # 查询当前 catalog 概览与版本历史 printf '{"jsonrpc":"2.0","id":3,"method":"catalog.status"}\n' \ | socat - UNIX-CONNECT:/tmp/bat-pid/bat.sock printf '{"jsonrpc":"2.0","id":4,"method":"catalog.versions"}\n' \ | socat - UNIX-CONNECT:/tmp/bat-pid/bat.sock # 分页读取当前版本的下载 manifest printf '{"jsonrpc":"2.0","id":5,"method":"resource.manifest","params":{"offset":0,"limit":50}}\n' \ | socat - UNIX-CONNECT:/tmp/bat-pid/bat.sock ```