fix(env-sync): 同步 provider_config.json(UI 分组/顺序/模型级配置)

问题:只同步 v2/config.json 时,只在 v2/provider_config.json 里存在的
provider 分组到对面就是缺失的 —— 表现为「provider 数量对,但 UI 分组少几个」。
实测:9 个分组里有 4 个(GINKA API / zxcbug / zxcbug-p / d1)纯规则型,
密钥只存在 provider_config.json 的 config.access.apiKey 里,旧实现完全没收集。

修复:
- sync-core:新增 V2_PROVIDER_CONFIG、extract/sanitize/collectProviderConfigSecrets、
  providerConfigKeyManifest;密钥命名空间 PROVIDER_CONFIG/<providerId>/<key>
- export:导出 v2.provider-config.json(脱敏),密钥并入加密束,manifest 记摘要与哈希
- import:按 providerId 合并规则、回填密钥空位、按 (providerId,modelId) 合并模型级配置、
  providerOrder 以快照为准并保留本机特有分组;重复导入幂等
- import:provider_config 路径补「护住本机已有密钥 N 个」提示(原来只在 config.json 侧有)
- 测试:单元 6 项 + E2E 2 项(分组完整同步/强制覆盖),全量 72 项通过
This commit is contained in:
2026-09-21 13:00:10 +08:00
parent 6ed5dda847
commit 06891fa726
6 changed files with 450 additions and 10 deletions
+21 -6
View File
@@ -107,7 +107,7 @@ latest.json # 指向最新快照
机器特有的 host/port/command 可写 `~/.zcode-env-sync/overrides.json`(**不进仓**),
导入时自动深度合并覆盖。
## 模型 provider(默认关)
## 模型 provider 与 UI 分组(默认关)
`~/.zcode/v2/config.json` 里通常是**活的 apiKey**,所以默认完全不碰(快照里无模型)。
需要同步时导出导入两边都显式开:
@@ -117,18 +117,33 @@ ZCODE_SYNC_WITH_MODELS=1 node export.mjs # 导出:脱敏结构明文 + 真密
ZCODE_SYNC_WITH_MODELS=1 node import.mjs # 导入:结构合并 + 密钥回填
```
导出:`v2.providers.json` 脱敏版明文(密钥位置是 `${MODEL_SECRET_REF}`),
真密钥以 `MODELS/<provider>/<key>` 条目并入 `secrets.enc`;
manifest 只记摘要 + 密钥哈希(`modelKeys`),不记值。
### 两份文件都要同步(只同步一份会导致「UI 分组少了」)
导入(`ZCODE_SYNC_WITH_MODELS=1` 时才动 `v2/config.json`,先备份):
| 文件 | 管什么 | 快照里的名字 |
|---|---|---|
| `v2/config.json` 的 `provider` | provider **能不能调**(kind、options、models 定义) | `v2.providers.json` |
| `v2/provider_config.json` | UI **怎么分组显示**(providerOrder、group、access、personalModelIds、modelOrder,以及 contextWindow 等模型级配置) | `v2.provider-config.json` |
- 快照有、本机无的 provider:整体新增
两份各带一份 `access.apiKey`,**不保证重叠**:有些分组只存在于 `provider_config.json`
(在 UI 里建了但没落进 `config.json`)。只同步前者时那些分组到对面就是缺失的 ——
表现为「provider 数量对,但 UI 分组少几个」。所以现在两份都导出、都回填。
导出:两个文件都写**脱敏版明文**(密钥位置是 `${MODEL_SECRET_REF}`),
真密钥分别以 `MODELS/<provider>/<key>` 与 `PROVIDER_CONFIG/<providerId>/<key>`
条目并入 `secrets.enc`;manifest 只记摘要 + 密钥哈希(`modelKeys` / `providerConfigKeys`),不记值。
导入(`ZCODE_SYNC_WITH_MODELS=1` 时才动这两个文件,先备份):
- 快照有、本机无的 provider / 分组:整体新增
- 已有 provider:只补缺的模型定义与非密钥字段,不碰本机已有密钥
- 已有分组:按 `providerId` 合并,补缺失字段(含 `group`/`personalModelIds`/`modelOrder`)
- 模型级配置:按 `(providerId, modelId)` 去重合并
- `providerOrder`:以快照顺序为准,本机特有的分组追加尾部(不丢本机分组)
- 密钥回填:只填**空位**(占位符/空/过短);本机已有密钥默认跳过保护,
确需覆盖再加 `ZCODE_SYNC_OVERWRITE_MODEL_SECRETS=1`
- 选中态(当前用哪个 provider/model)默认不动,
加 `ZCODE_SYNC_APPLY_MODEL_SELECTION=1` 才应用(同样先备份 `v2/setting.json`)
- 重复导入幂等:不会重复追加分组或模型级配置
| 变量 | 说明 |
|---|---|