mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-27 16:26:16 +08:00
feat(provider): support additive provider key lifecycle management
Add `addToLive` parameter to add_provider so callers can opt out of writing to the live config (e.g. when duplicating an inactive provider). Add `originalId` parameter to update_provider to support provider key renames — the old key is removed from live config before the new one is written. Frontend: ProviderForm now exposes provider-key input for openclaw app type, and EditProviderDialog forwards originalId on save. Deep-link import passes addToLive=true to preserve existing behavior.
This commit is contained in:
@@ -36,9 +36,11 @@ pub fn add_provider(
|
||||
state: State<'_, AppState>,
|
||||
app: String,
|
||||
provider: Provider,
|
||||
#[allow(non_snake_case)] addToLive: Option<bool>,
|
||||
) -> Result<bool, String> {
|
||||
let app_type = AppType::from_str(&app).map_err(|e| e.to_string())?;
|
||||
ProviderService::add(state.inner(), app_type, provider).map_err(|e| e.to_string())
|
||||
ProviderService::add(state.inner(), app_type, provider, addToLive.unwrap_or(true))
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -46,9 +48,11 @@ pub fn update_provider(
|
||||
state: State<'_, AppState>,
|
||||
app: String,
|
||||
provider: Provider,
|
||||
#[allow(non_snake_case)] originalId: Option<String>,
|
||||
) -> Result<bool, String> {
|
||||
let app_type = AppType::from_str(&app).map_err(|e| e.to_string())?;
|
||||
ProviderService::update(state.inner(), app_type, provider).map_err(|e| e.to_string())
|
||||
ProviderService::update(state.inner(), app_type, originalId.as_deref(), provider)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -110,7 +110,7 @@ pub fn import_provider_from_deeplink(
|
||||
let provider_id = provider.id.clone();
|
||||
|
||||
// Use ProviderService to add the provider
|
||||
ProviderService::add(state, app_type.clone(), provider)?;
|
||||
ProviderService::add(state, app_type.clone(), provider, true)?;
|
||||
|
||||
// Add extra endpoints as custom endpoints (skip first one as it's the primary)
|
||||
for ep in all_endpoints.iter().skip(1) {
|
||||
|
||||
@@ -33,6 +33,21 @@ pub(crate) fn sanitize_claude_settings_for_live(settings: &Value) -> Value {
|
||||
v
|
||||
}
|
||||
|
||||
pub(crate) fn provider_exists_in_live_config(
|
||||
app_type: &AppType,
|
||||
provider_id: &str,
|
||||
) -> Result<bool, AppError> {
|
||||
match app_type {
|
||||
AppType::OpenCode => crate::opencode_config::get_providers()
|
||||
.map(|providers| providers.contains_key(provider_id))
|
||||
.map_err(Into::into),
|
||||
AppType::OpenClaw => crate::openclaw_config::get_providers()
|
||||
.map(|providers| providers.contains_key(provider_id))
|
||||
.map_err(Into::into),
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn json_is_subset(target: &Value, source: &Value) -> bool {
|
||||
match source {
|
||||
Value::Object(source_map) => {
|
||||
|
||||
@@ -29,7 +29,8 @@ pub use live::{
|
||||
pub(crate) use live::sanitize_claude_settings_for_live;
|
||||
pub(crate) use live::{
|
||||
build_effective_settings_with_common_config, normalize_provider_common_config_for_storage,
|
||||
strip_common_config_from_live_settings, sync_current_provider_for_app_to_live,
|
||||
provider_exists_in_live_config, strip_common_config_from_live_settings,
|
||||
sync_current_provider_for_app_to_live,
|
||||
write_live_with_common_config,
|
||||
};
|
||||
|
||||
@@ -166,7 +167,12 @@ impl ProviderService {
|
||||
}
|
||||
|
||||
/// Add a new provider
|
||||
pub fn add(state: &AppState, app_type: AppType, provider: Provider) -> Result<bool, AppError> {
|
||||
pub fn add(
|
||||
state: &AppState,
|
||||
app_type: AppType,
|
||||
provider: Provider,
|
||||
add_to_live: bool,
|
||||
) -> Result<bool, AppError> {
|
||||
let mut provider = provider;
|
||||
// Normalize Claude model keys
|
||||
Self::normalize_provider_if_claude(&app_type, &mut provider);
|
||||
@@ -176,7 +182,7 @@ impl ProviderService {
|
||||
// Save to database
|
||||
state.db.save_provider(app_type.as_str(), &provider)?;
|
||||
|
||||
// Additive mode apps (OpenCode, OpenClaw) - always write to live config
|
||||
// Additive mode apps (OpenCode, OpenClaw): optionally write to live config.
|
||||
if app_type.is_additive_mode() {
|
||||
// OMO / OMO Slim providers use exclusive mode and write to dedicated config file.
|
||||
if matches!(app_type, AppType::OpenCode)
|
||||
@@ -186,6 +192,9 @@ impl ProviderService {
|
||||
// Users must explicitly switch/apply an OMO provider to activate it.
|
||||
return Ok(true);
|
||||
}
|
||||
if !add_to_live {
|
||||
return Ok(true);
|
||||
}
|
||||
write_live_with_common_config(state.db.as_ref(), &app_type, &provider)?;
|
||||
return Ok(true);
|
||||
}
|
||||
@@ -207,18 +216,59 @@ impl ProviderService {
|
||||
pub fn update(
|
||||
state: &AppState,
|
||||
app_type: AppType,
|
||||
original_id: Option<&str>,
|
||||
provider: Provider,
|
||||
) -> Result<bool, AppError> {
|
||||
let mut provider = provider;
|
||||
let original_id = original_id.unwrap_or(provider.id.as_str()).to_string();
|
||||
let provider_id_changed = original_id != provider.id;
|
||||
// Normalize Claude model keys
|
||||
Self::normalize_provider_if_claude(&app_type, &mut provider);
|
||||
Self::validate_provider_settings(&app_type, &provider)?;
|
||||
normalize_provider_common_config_for_storage(state.db.as_ref(), &app_type, &mut provider)?;
|
||||
|
||||
if provider_id_changed {
|
||||
if !app_type.is_additive_mode() {
|
||||
return Err(AppError::Message(
|
||||
"Only additive-mode providers support changing provider key".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if provider_exists_in_live_config(&app_type, &original_id)? {
|
||||
return Err(AppError::Message(
|
||||
"Provider key cannot be changed after the provider has been added to the app config"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if state
|
||||
.db
|
||||
.get_provider_by_id(&provider.id, app_type.as_str())?
|
||||
.is_some()
|
||||
|| provider_exists_in_live_config(&app_type, &provider.id)?
|
||||
{
|
||||
return Err(AppError::Message(format!(
|
||||
"Provider '{}' already exists in app '{}'",
|
||||
provider.id,
|
||||
app_type.as_str()
|
||||
)));
|
||||
}
|
||||
|
||||
state.db.save_provider(app_type.as_str(), &provider)?;
|
||||
state.db.delete_provider(app_type.as_str(), &original_id)?;
|
||||
|
||||
if crate::settings::get_current_provider(&app_type).as_deref() == Some(&original_id) {
|
||||
crate::settings::set_current_provider(&app_type, Some(provider.id.as_str()))?;
|
||||
}
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Save to database
|
||||
state.db.save_provider(app_type.as_str(), &provider)?;
|
||||
|
||||
// Additive mode apps (OpenCode, OpenClaw) - always update in live config
|
||||
// Additive mode apps (OpenCode, OpenClaw): only sync to live when the provider
|
||||
// already exists in live config. Editing a DB-only provider must not auto-add it.
|
||||
if app_type.is_additive_mode() {
|
||||
if matches!(app_type, AppType::OpenCode) && provider.category.as_deref() == Some("omo")
|
||||
{
|
||||
@@ -250,6 +300,9 @@ impl ProviderService {
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
if !provider_exists_in_live_config(&app_type, &provider.id)? {
|
||||
return Ok(true);
|
||||
}
|
||||
write_live_with_common_config(state.db.as_ref(), &app_type, &provider)?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user