mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 07:24:55 +08:00
376 lines
20 KiB
Markdown
376 lines
20 KiB
Markdown
# Rust Resource Backend RPC API
|
||
|
||
本文档冻结本机 Rust Resource Backend API 的稳定调用边界。Go 项目
|
||
`bat-api`、Go 服务层、运维脚本和 `bat` CLI 都应以这里的 JSON-RPC
|
||
contract 为准,不应绕过 daemon 状态文件或扩展 `bat-ffi` 作为主路径。
|
||
|
||
## 传输
|
||
|
||
- 传输:Unix domain socket。
|
||
- 默认 socket:`/tmp/bat-pid/bat.sock`。
|
||
- 协议:JSON-RPC 2.0,每行一个 request,每行一个 response。
|
||
- 编码:UTF-8 JSON。
|
||
- 访问控制:依赖本机文件权限和状态目录权限;不要把 socket 暴露到公网。
|
||
|
||
请求:
|
||
|
||
```json
|
||
{"jsonrpc":"2.0","id":1,"method":"resource.repair","params":null}
|
||
```
|
||
|
||
成功响应的 JSON-RPC 顶层 `result` 一律是应用层 envelope:
|
||
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 1,
|
||
"result": {
|
||
"ok": true,
|
||
"status": "accepted",
|
||
"data": {"task_id": "task-1234-1", "kind": "resource.repair"},
|
||
"request_id": "req-1234-1"
|
||
}
|
||
}
|
||
```
|
||
|
||
应用层失败也放在 `result` 的 envelope 中:
|
||
|
||
```json
|
||
{
|
||
"ok": false,
|
||
"status": "error",
|
||
"error": {
|
||
"code": "BAT-ERR-700003",
|
||
"kind": "not_implemented",
|
||
"domain": "rpc",
|
||
"location": "rpc.dispatch",
|
||
"message": "方法尚未实现:daemon.clean-stable",
|
||
"retryable": false
|
||
},
|
||
"request_id": "req-1234-2"
|
||
}
|
||
```
|
||
|
||
只有 JSON 解析失败等传输层错误使用 JSON-RPC 顶层 `error`。
|
||
|
||
## Envelope
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---|---|---|
|
||
| `ok` | bool | 应用层是否成功。 |
|
||
| `status` | string | `ok`、`accepted` 或 `error`。 |
|
||
| `data` | object/null | 成功结果。失败时省略。 |
|
||
| `error` | object/null | `ApiError`。成功时省略。 |
|
||
| `request_id` | string | daemon 进程内请求 ID,用于日志关联。 |
|
||
|
||
`ApiError` 结构以 `core/src/error_code.rs` 码表为准:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---|---|---|
|
||
| `code` | string | `BAT-ERR-<6位>`。 |
|
||
| `kind` | string | 错误类别。 |
|
||
| `domain` | string | 错误域。 |
|
||
| `location` | string | Rust 侧出错位置。 |
|
||
| `message` | string | 可诊断错误信息。 |
|
||
| `retryable` | bool | 调用方是否可以按策略重试。 |
|
||
|
||
## 方法
|
||
|
||
### daemon
|
||
|
||
| 方法 | 状态 | params | data |
|
||
|---|---|---|---|
|
||
| `daemon.status` | 已实现 | `null` | 后台状态报告。 |
|
||
| `daemon.logs` | 已实现 | `{ "tail": 200 }` | 日志尾部报告。 |
|
||
| `daemon.stop` | 已实现 | `null` | accepted ack。 |
|
||
| `daemon.restart` | 已实现 | `null` | accepted ack;启动 Rust lifecycle controller,并在响应后停止当前 daemon。 |
|
||
| `daemon.reload` | 已实现 | `null` | accepted ack。 |
|
||
| `daemon.refresh` | 已实现 | `{ "force": false }` | accepted ack。 |
|
||
| `daemon.doctor` | 已实现 | `null` | 只读诊断报告。 |
|
||
| `daemon.clean-stable` | 保留 | `null` | live RPC 不执行;由 CLI 离线清理入口处理。 |
|
||
|
||
`daemon.restart` 不在 daemon 线程内手写第二套启动流程;它启动本机 Rust
|
||
`bat restart --state-dir ...` lifecycle controller,由既有 CLI restart 路径复用
|
||
保存的启动参数、代理凭据、PID/socket 替换和控制锁。
|
||
|
||
`bat.status`、`bat.stop`、`bat.restart`、`bat.reload`、`bat.refresh`、`bat.logs`、
|
||
`bat.doctor`、`bat.clean-stable` 是兼容别名;新代码应使用 `daemon.*`。
|
||
|
||
### resource
|
||
|
||
| 方法 | 状态 | params | data |
|
||
|---|---|---|---|
|
||
| `resource.state` | 已实现 | `null` | 资源发布根、版本状态、上次同步结果。 |
|
||
| `resource.sync` | 已实现 | `{ "force": false }` | `{ "task_id": "...", "kind": "resource.sync" }`。 |
|
||
| `resource.verify` | 已实现 | `null` | `{ "task_id": "...", "kind": "resource.verify" }`。 |
|
||
| `resource.repair` | 已实现 | `null` | `{ "task_id": "...", "kind": "resource.repair" }`。 |
|
||
| `resource.manifest` | 已实现 | `{ "offset": 0, "limit": 100 }` | 当前 download manifest 分页。 |
|
||
| `resource.list` | 已实现 | `{ "offset": 0, "limit": 100 }` | `resource.manifest` 的兼容别名。 |
|
||
| `resource.index` | 已实现 | `{ "offset": 0, "limit": 100, "type": "asset_bundle", "hash": "...", "path_pattern": "*", "release_id": "...", "platform": "windows", "destination": "...", "archive_entry": "...", "parse_status": "parsed", "format": "json" }` | 当前 `ResourceRepository` 分页/过滤查询。 |
|
||
|
||
`resource.repair` 会开启本地 manifest audit + repair,不继承 `force`。
|
||
`resource.manifest` / `resource.list` 查询当前已发布 release 的
|
||
`official-download-manifest.json`;`resource.index` 查询可选导入产生的
|
||
SQLite `ResourceRepository`,索引不存在时返回 `ok=true` 且
|
||
`data.available=false`,不会隐式创建数据库。`resource.index` 可按
|
||
`resource_type`/`type`、`hash`、`path_pattern`、`official_release_id`/`release_id`、
|
||
`platform`、`destination`、`bundle_path`、`archive_entry`、`parse_status` 和
|
||
`text_unit_format`/`format` 过滤;`path_id`、`class_id` 和 `field_path`
|
||
属于 `parse.text_units` / `parse.errors` 的对象级查询。`limit` 范围是
|
||
`1..=1000`,非法参数返回 `BAT-ERR-700002`。
|
||
|
||
`resource.index` 的 `entries[]` 是 `Resource` JSON,除 `id`、`local_path`、
|
||
`entry` 外会包含 `metadata`:`official_release_id`、`platform`、
|
||
`bundle_path`、`archive_entries`、`parse_statuses`、`unity_versions`、
|
||
`text_assets`、`text_unit_count`、`text_unit_formats` 和
|
||
`text_unit_error_count` 等字段。旧索引库会通过 `metadata_json` 迁移列得到
|
||
默认空 metadata。
|
||
|
||
`resource.state`、`catalog.status`、`parse.status` 和 `localized.status`
|
||
都会返回当前观察面的短状态 `status` 与稳定状态码 `status_code`。`status_code`
|
||
使用命名空间格式,例如 `official.up_to_date`、`official.published`、
|
||
`parse.completed`、`translation.queued_offline`、`localized.published` 和
|
||
`distribution.ready`。这些状态码描述资源/解析/翻译 handoff/汉化/分发生命周期;
|
||
失败原因仍使用 `BAT-ERR-*` 错误码,二者不混用。响应还会包含
|
||
`status_phase`、`status_terminal` 和 `status_retryable`,供 `bat-api` 等读侧
|
||
决定展示、重试或 readiness。
|
||
|
||
官方资源完整新版本发布后,Rust 侧会先比较上一完整 release 与当前 release
|
||
的 download manifest,并在当前 release 根目录写出:
|
||
|
||
- `official-resource-changes.json`:记录 added / modified / removed 资源。
|
||
同一 destination 只有 size 或 BLAKE3 变化才算 modified;URL 或 CDN root
|
||
变化但内容一致时不进入解析/翻译候选。
|
||
- `crowdin-translation-handoff.json`:只包含 added + modified 资源,作为后续
|
||
Crowdin worker 的稳定本地队列输入;当前 RPC 不直接调用 Crowdin API。
|
||
- `official-parse-cache.json`:解析缓存。up-to-date 轮询发现本地文件未变且缓存
|
||
有效时只读取摘要,不重复解析。
|
||
- `official-textunit-index.json`:TextUnit 明细和解析错误索引。up-to-date 轮询发现
|
||
本地文件未变且索引有效时复用,不重复解析。
|
||
- `official-textunit-tasks.json`:只由 added + modified 资源、parse cache 和
|
||
TextUnit 明细索引派生,记录 TextUnit 任务、跳过原因和解析诊断。
|
||
- `crowdin-textunit-queue.json`:只包含已产生 TextUnit 的离线任务,预留给后续
|
||
Crowdin worker;当前不会发出网络请求。
|
||
- `translation-tasks.sqlite`:当前 release 的可变 worker 状态库,记录
|
||
queued / running / failed / completed / skipped、attempt count、provider run
|
||
ID 和 failure reason;schema 由 `schema_migrations` 版本表管理。
|
||
- `translation-handoff.json`:当前 release 的版本化 job/unit/provider run 交接
|
||
快照;worker 更新后的实时状态仍以 `translation-tasks.sqlite` 为准。
|
||
|
||
删除资源只进入 `official-resource-changes.json`,不进入 Crowdin handoff。
|
||
|
||
### parse
|
||
|
||
| 方法 | 状态 | params | data |
|
||
|---|---|---|---|
|
||
| `parse.status` | 已实现 | `null` | 当前官方 release 的解析缓存状态。 |
|
||
| `parse.text_units` | 已实现 | `{ "offset": 0, "limit": 100, "destination": "*Table*", "archive_entry": "*.bytes", "path_id": 1, "class_id": 114, "field_path": "*Text*", "format": "json" }` | 当前官方 release 的 TextUnit 明细分页。 |
|
||
| `parse.errors` | 已实现 | `{ "offset": 0, "limit": 100, "destination": "*Table*", "archive_entry": "*.bytes", "path_id": 1, "class_id": 114, "field_path": "*Text*", "format": "json" }` | 当前官方 release 的解析错误分页。 |
|
||
| `translation.tasks` | 已实现 | `{ "offset": 0, "limit": 100, "task_id": "...", "release_id": "...", "destination": "...", "archive_entry": "...", "status": "skipped_parse_failed", "parse_status": "failed", "format": "json", "has_reason": true }` | 当前官方 release 的离线 TextUnit 翻译任务状态分页。 |
|
||
| `translation.handoff` | 已实现 | `null` | 当前官方 release 的 job、unit、provider run 交接视图;动态合并队列和 SQLite worker 状态。 |
|
||
| `translation.task.update` | 已实现 | `{ "task_id": "...", "status": "failed", "failure_reason": "...", "provider_run_id": "..." }` | 写入当前 release 的 provider worker 状态,返回可回查任务记录。 |
|
||
|
||
`parse.status` 是只读查询;没有当前 release 或没有解析缓存时返回
|
||
`ok=true` 且 `data.available=false`。解析缓存来自官方原版资源目录,不读取
|
||
汉化输出目录。存在 `official-textunit-index.json` 时,响应会包含
|
||
`textunit_index_available=true`、`textunit_index_path` 和
|
||
`textunit_index_summary`;存在 `official-textunit-tasks.json` 时,响应会包含
|
||
`textunit_queue_available=true`、`textunit_task_queue_path` 和
|
||
`textunit_task_summary`。当 TextUnit 队列存在且有离线任务时,
|
||
`translation_status_code=translation.queued_offline`;真实 Crowdin worker
|
||
尚未接入时不会返回翻译完成状态。
|
||
|
||
`parse.text_units` / `parse.errors` 是只读查询;没有当前 release 或没有
|
||
`official-textunit-index.json` 时返回 `ok=true` 且 `data.available=false`。
|
||
分页参数 `offset` 默认 0,`limit` 默认 100,范围是 `1..=1000`。过滤参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---|---|---|
|
||
| `destination` | string | official download manifest destination,支持 `*` 通配。 |
|
||
| `archive_entry` | string | zip 内条目,支持 `*` 通配;直接 bundle 通常为 `null`。 |
|
||
| `path_id` | integer | Unity object path id。 |
|
||
| `class_id` | integer | Unity class id。 |
|
||
| `field_path` | string | TypeTree/TextAsset 字段路径,支持 `*` 通配。 |
|
||
| `format` | string | TextUnit 格式,例如 `json`、`csv`、`tsv`、`plain` 或 `typetree_string`。 |
|
||
|
||
`parse.text_units` 的 `entries[]` 会包含 source text、source URL、
|
||
destination、archive entry、source kind、Unity version、serialized file、
|
||
path id、class id、field path、字段 offset/byte size、format、asset name 和
|
||
context。`parse.errors` 的 `entries[]` 会包含 source URL、destination、
|
||
archive entry、status、serialized file、path id、class id、field path、
|
||
offset 和 error。TypeTree-covered managed reference 字段会进入结构化字段遍历;
|
||
完整 managed reference registry 等暂不支持结构会进入解析错误,而不是静默降级为
|
||
低保真文本。
|
||
|
||
`translation.tasks` 优先查询当前 release 的 `translation-tasks.sqlite`,旧 release
|
||
没有该文件时回退到 `official-textunit-tasks.json`;用于查看离线 TextUnit
|
||
翻译任务候选和 provider worker 状态。没有当前 release 或没有任务队列时返回
|
||
`ok=true` 且 `data.available=false`。过滤参数包括 `task_id`、
|
||
`official_release_id`/`release_id`、`destination`、`path_pattern`、
|
||
`archive_entry`、`status`/`task_status`、`worker_status`、`parse_status`、
|
||
`text_unit_format`/`format`、`has_reason` 和 `has_failure_reason`。
|
||
`entries[]` 会包含 `official_release_id`、`destination`、`archive_entry`、
|
||
`parse_status`、队列 `status`、`task_status`、`failure_reason`、`attempt_count`、
|
||
`provider_run_id`、TextAsset/TextUnit 摘要和校验指纹。
|
||
|
||
`translation.task.update` 只更新当前 release 的 SQLite 状态库,不改写 immutable
|
||
队列文件,也不主动访问 Crowdin。`status` 支持 `queued`、`running`、`failed`、
|
||
`completed` 和 `skipped`;进入 `running` 会增加 attempt count,`completed` 会
|
||
记录完成时间,`failed` 可写入 `failure_reason`。因此 worker 消费 handoff 后,
|
||
bat-api 可通过 `translation.tasks` 查询单项任务,也可通过
|
||
`translation.handoff` 获取完整 job/unit/provider run 状态。`translation.handoff`
|
||
不会触发下载或 provider 网络请求;没有当前 release 或任务队列时返回
|
||
`data.available=false`。
|
||
|
||
### localized
|
||
|
||
| 方法 | 状态 | params | data |
|
||
|---|---|---|---|
|
||
| `localized.status` | 已实现 | `null` | 汉化发布状态、当前官方 release 匹配关系和汉化输出目录。 |
|
||
|
||
`localized.status` 严格按 daemon / `.env` 中的 `BAT_LOCALIZED_OUTPUT` 或
|
||
`--localized-output` 查询汉化产物目录,不把 `./bat-resources` 与
|
||
`./bat-localized` 混用。当前支持未汉化发布状态和已汉化发布状态的只读报告。
|
||
`status` / `status_code` 使用生命周期短状态和稳定状态码,例如
|
||
`pending` / `localized.pending`、`stale` / `localized.stale`、`published` /
|
||
`localized.published`;旧的 `localized` / `not_localized` 业务标签放在
|
||
`localized_release_status`。返回 `localized_release_status=localized` 的条件是:
|
||
`localized-version-state.json` 的官方 release ID 匹配当前官方 release,
|
||
`current` symlink 指向汉化发布根下对应的 `versions/<id>`,并且该版本目录中的
|
||
`localized-patch-manifest.json` 存在且 release ID 匹配。响应会返回
|
||
`patch_manifest_path`、`patch_manifest_available`、
|
||
`patch_manifest_matches_release`、`patch_file_count`、
|
||
`patch_text_asset_operation_count` 和 `rollback_previous_current_target`。
|
||
|
||
### catalog
|
||
|
||
| 方法 | 状态 | params | data |
|
||
|---|---|---|---|
|
||
| `catalog.status` | 已实现 | `null` | 当前已发布 catalog 概览。 |
|
||
| `catalog.versions` | 已实现 | `null` | current / in_progress / previous / failed。 |
|
||
| `catalog.diff` | 已实现 | `null` | 当前 snapshot 相对上一可用版本的差异。 |
|
||
| `catalog.refresh` | 已实现 | `{ "force": false }` | `{ "task_id": "...", "kind": "catalog.refresh" }`。 |
|
||
|
||
只读查询在没有可用版本时返回 `ok=true` 且 `data.available=false`。
|
||
`catalog.status` 可用时会返回 `status_code=official.published`,并用
|
||
`distribution_status_code=distribution.ready` 表示该官方 release 可被读侧分发;
|
||
不可用时对应 `official.unavailable` / `distribution.blocked`。
|
||
|
||
### task
|
||
|
||
| 方法 | 状态 | params | data |
|
||
|---|---|---|---|
|
||
| `task.status` | 已实现 | `{ "task_id": "..." }` | 单个任务记录。 |
|
||
| `task.list` | 已实现 | `null` | `{ "tasks": [...] }`。 |
|
||
| `task.cancel` | 已实现 | `{ "task_id": "..." }` | cancel ack。 |
|
||
| `task.logs` | 已实现 | `{ "task_id": "..." }` | `{ "task_id": "...", "lines": [...] }`。 |
|
||
| `task.create` | 保留 | object | 不开放通用任务入口;由语义方法创建任务。 |
|
||
|
||
任务记录:
|
||
|
||
```json
|
||
{
|
||
"id": "task-1234-1",
|
||
"kind": "resource.repair",
|
||
"status": "queued",
|
||
"stage": null,
|
||
"message": null,
|
||
"created_at": 1780000000,
|
||
"updated_at": 1780000000,
|
||
"started_at": null,
|
||
"finished_at": null,
|
||
"error": null,
|
||
"result": null
|
||
}
|
||
```
|
||
|
||
`status` 取值:`queued`、`running`、`succeeded`、`failed`、`cancelled`。
|
||
daemon 重启后仍处于 `queued` 或 `running` 的历史任务会被标记为
|
||
`failed`,错误码为 `BAT-ERR-700005`。
|
||
|
||
### patch / unityfs
|
||
|
||
已开放的文件级写入方法:
|
||
|
||
- `patch.apply`:对显式 `source_path`、`patch_path`、`target_path` 执行
|
||
Binary/JSON/Text patch apply,`kind` 取值为 `binary`、`json` 或 `text`。
|
||
- `unityfs.patch_text_asset`:对显式 UnityFS `bundle_path` 中的
|
||
`serialized_file_path` / `path_id` TextAsset 应用 `replacement_path`,写入
|
||
`target_path`,可选 `expected_name`。
|
||
- `unityfs.patch_string_field`:对显式 UnityFS `bundle_path` 中的
|
||
`serialized_file_path` / `path_id` / `field_path` TypeTree string 字段应用
|
||
`replacement_text` 或 UTF-8 `replacement_path`,写入 `target_path`,可选
|
||
`expected_value`。
|
||
- `unityfs.patch_field`:对显式 UnityFS `bundle_path` 中的
|
||
`serialized_file_path` / `path_id` / `field_path` TypeTree 字段应用语义
|
||
`replacement` JSON,写入 `target_path`,可选 `expected_value`。`replacement`
|
||
使用 `{"kind":"signed","value":42}` 这类 tagged JSON;支持
|
||
`bool`、`signed`、`unsigned`、`float32`、`float64`、`string`、`bytes`、
|
||
`enum`、`bit_field`、`p_ptr`、固定 Unity 叶子结构、object 字段组合和 TypeTree schema 支撑的 array/map 整体替换。enum 形如
|
||
`{"kind":"enum","value":{"type_name":"ScenarioDifficulty","storage_type":"int","value":3}}`;
|
||
`type_name` 是 TypeTree enum 类型名,`storage_type` 是 backing integer 类型。`LayerMask` / `BitField` 形如
|
||
`{"kind":"bit_field","value":{"type_name":"LayerMask","storage_type":"UInt32","bits":9}}`。
|
||
array 形如
|
||
`{"kind":"array","value":[{"kind":"string","value":"你好"}]}`;map entry 用
|
||
object 表达,例如
|
||
`{"kind":"object","value":[{"name":"first","value":{"kind":"string","value":"jp"}}]}`。
|
||
扩容时复用当前首个元素或 TypeTree data node 的编码 schema;map entry schema
|
||
变化、unknown 字段和未覆盖的 managed reference registry 变体仍会返回明确错误。
|
||
固定 Unity 叶子结构使用 raw bits/bytes 表达,例如
|
||
`{"kind":"float32_struct","value":{"type_name":"Vector3f","values":[1065353216,1073741824,1077936128]}}`
|
||
或
|
||
`{"kind":"fixed_bytes","value":{"type_name":"GUID","bytes":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]}}`。
|
||
|
||
这些方法同步执行,不进入 `task.*` 队列;输出文件使用临时文件原子写入,响应
|
||
`data` 会返回 source / patch 或 replacement / target 的 size 与 BLAKE3。`target_path`
|
||
不能与输入文件相同。
|
||
|
||
仍关闭的范围:发布级 `patch build` / `patch rollback`、复杂 UnityFS 语义编辑、
|
||
`unityfs.inspect`、通用 manifest 驱动 release 切换。调用这些规划方法仍返回
|
||
`BAT-ERR-700003`。
|
||
|
||
CLI 对应关系:
|
||
|
||
| CLI | RPC |
|
||
|---|---|
|
||
| `bat patch-apply` | `patch.apply` |
|
||
| `bat unityfs-patch-text-asset` | `unityfs.patch_text_asset` |
|
||
| `bat unityfs-patch-string-field` | `unityfs.patch_string_field` |
|
||
| `bat unityfs-patch-field` | `unityfs.patch_field` |
|
||
|
||
## Go 调用边界
|
||
|
||
`bat-api` 应直接调用本 RPC contract,不通过 `exec` 调用 `bat` binary。
|
||
`bat` binary 是人类 CLI 和进程生命周期工具;默认 `refresh` / `repair`
|
||
在 daemon 可用时也会作为 RPC client 调用同一个 socket。`daemon.restart`
|
||
会启动 Rust lifecycle controller 复用同一套 CLI restart 路径,Go 层仍不直接
|
||
`exec` 或解析 `bat` stdout。
|
||
|
||
人类 CLI 的只读查询命令与 RPC 对应关系如下:
|
||
|
||
| CLI | RPC |
|
||
|---|---|
|
||
| `bat parse-status` | `parse.status` |
|
||
| `bat parse-text-units` | `parse.text_units` |
|
||
| `bat parse-errors` | `parse.errors` |
|
||
| `bat localized-status` | `localized.status` |
|
||
| `bat resource-index` | `resource.index` |
|
||
|
||
`bat resource-index` 支持 `--offset`、`--limit`、`--resource-type`、`--hash`
|
||
和 `--path-pattern`;`bat parse-text-units` / `bat parse-errors` 支持
|
||
`--offset`、`--limit`、`--destination`、`--archive-entry`、`--path-id`、
|
||
`--class-id`、`--field-path` 和 `--format`;这些过滤参数不适用于
|
||
`parse-status` 或 `localized-status`。
|
||
|
||
Go mirror contract fixture 固化在 `internal/api/testdata/contract/`,覆盖
|
||
`catalog.status` available/unavailable、`resource.manifest` page0 和对应
|
||
`official-sync-snapshot.json`。这些 fixture 由 Rust 输出归一化而来,只用于
|
||
schema / mirror 回归;live daemon socket 和完整 release 切换仍需在允许 smoke 的
|
||
隔离环境中验证。
|
||
|
||
禁止事项:
|
||
|
||
- Go 服务层不直接读写 `bat-status.json`、`bat-tasks.json` 等 daemon 内部状态文件。
|
||
- Go 服务层不扩展 `bat-ffi` 为主控制面。
|
||
- Go 服务层不通过 stdout 解析 `bat status --json` 作为常规调用路径。
|