fix: preserve Codex model catalog on live read of active provider

Editing the currently active Codex provider triggered `read_live_settings`,
which returned only { auth, config }, omitting `modelCatalog`. The form's
mapping table then initialized to empty, and a subsequent save wiped the
DB's `modelCatalog` field — silently destroying user-configured model
mappings after every CC Switch restart.

The mappings already live on disk as a projection in
`~/.codex/cc-switch-model-catalog.json` (pointed at by `config.toml`'s
`model_catalog_json`). Reverse-parse that file so live reads return the
same shape the save path writes.

- Add `read_codex_model_catalog_simplified_from_live` to recover
  `{ model, displayName?, contextWindow? }` entries from the catalog file.
- Skip user-managed external catalogs (filename != cc-switch-model-catalog.json)
  so we don't downgrade their richer structure to the simplified table.
- Squash display_name == slug and context_window == default (config's
  `model_context_window`, or 128_000 fallback) so blank inputs round-trip
  back to blank instead of materializing fallback values in the UI.
- Collapse all failure modes (missing file, parse error, no matching
  field) to Ok(None) so the editor stays openable when the projection
  file is absent or corrupt.
- Wire the new function into `read_live_settings`'s Codex branch.
- Cover the new pure helpers with 7 unit tests in codex_config::tests.
This commit is contained in:
Jason
2026-05-20 17:36:55 +08:00
parent b44f83f7c5
commit ad8bdf16ae
2 changed files with 255 additions and 1 deletions
+14 -1
View File
@@ -961,7 +961,20 @@ pub fn read_live_settings(app_type: AppType) -> Result<Value, AppError> {
}
let auth: Value = read_json_file(&auth_path)?;
let cfg_text = crate::codex_config::read_and_validate_codex_config_text()?;
Ok(json!({ "auth": auth, "config": cfg_text }))
let mut result = json!({ "auth": auth, "config": cfg_text });
// `modelCatalog` is a cc-switch private field that lives only in
// the DB SSOT plus the `cc-switch-model-catalog.json` projection
// file — it is never inlined into `auth.json` or `config.toml`.
// Reverse-parse the projection so the edit form for the active
// Codex provider doesn't see an empty mapping table.
if let Ok(Some(model_catalog)) =
crate::codex_config::read_codex_model_catalog_simplified_from_live()
{
if let Some(obj) = result.as_object_mut() {
obj.insert("modelCatalog".to_string(), model_catalog);
}
}
Ok(result)
}
AppType::Claude => {
let path = get_claude_settings_path();