refactor(hermes): share provider-source marker constants and write guard

After /simplify review of the P1-3 second wave, two small cleanups:

- Lift the `_cc_source` / `providers_dict` magic strings out of
  ProviderCard into a shared helper (`isHermesReadOnlyProvider`) and
  named constants in hermesProviderPresets.ts. Front-end and back-end
  now document the same marker contract in two mirrored places
  instead of drifting strings.

- Replace the duplicate `is_dict_only_provider` + `format!` branches
  at the top of `set_provider` / `remove_provider` with a single
  `ensure_provider_writable(config, name, verb)` guard. Future error
  copy tweaks only have to happen once.

No behaviour change; all 52 hermes_config tests stay green.
This commit is contained in:
Jason
2026-04-20 10:43:23 +08:00
parent abb305a82f
commit f57edfd697
3 changed files with 44 additions and 13 deletions
+2 -3
View File
@@ -15,6 +15,7 @@ import SubscriptionQuotaFooter from "@/components/SubscriptionQuotaFooter";
import CopilotQuotaFooter from "@/components/CopilotQuotaFooter";
import CodexOauthQuotaFooter from "@/components/CodexOauthQuotaFooter";
import { PROVIDER_TYPES } from "@/config/constants";
import { isHermesReadOnlyProvider } from "@/config/hermesProviderPresets";
import { ProviderHealthBadge } from "@/components/providers/ProviderHealthBadge";
import { FailoverPriorityBadge } from "@/components/providers/FailoverPriorityBadge";
import { extractCodexBaseUrl } from "@/utils/providerConfigUtils";
@@ -183,9 +184,7 @@ export function ProviderCard({
// Hermes v12+ overlay entries live under the `providers:` dict and are
// read-only here — writes have to go through Hermes Web UI.
const isHermesReadOnly =
appId === "hermes" &&
(provider.settingsConfig as Record<string, unknown>)?._cc_source ===
"providers_dict";
appId === "hermes" && isHermesReadOnlyProvider(provider.settingsConfig);
const isCodexOauth =
provider.meta?.providerType === PROVIDER_TYPES.CODEX_OAUTH;
+23
View File
@@ -5,6 +5,29 @@
import type { ProviderCategory } from "../types";
import type { PresetTheme, TemplateValueConfig } from "./claudeProviderPresets";
/**
* Marker field and source values that `hermes_config.rs::get_providers`
* injects onto each settings payload. Kept in sync with the Rust constants
* `PROVIDER_SOURCE_FIELD` / `PROVIDER_SOURCE_CUSTOM_LIST` / `PROVIDER_SOURCE_DICT`.
*/
export const HERMES_PROVIDER_SOURCE_FIELD = "_cc_source";
export const HERMES_PROVIDER_SOURCE_CUSTOM_LIST = "custom_providers";
export const HERMES_PROVIDER_SOURCE_DICT = "providers_dict";
/**
* True when the provider was sourced from Hermes' v12+ `providers:` dict —
* CC Switch renders those read-only and routes edits to Hermes Web UI.
*/
export function isHermesReadOnlyProvider(settingsConfig: unknown): boolean {
if (!settingsConfig || typeof settingsConfig !== "object") {
return false;
}
const marker = (settingsConfig as Record<string, unknown>)[
HERMES_PROVIDER_SOURCE_FIELD
];
return marker === HERMES_PROVIDER_SOURCE_DICT;
}
/**
* A model entry under a Hermes custom_provider.
*