mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-02 02:05:57 +08:00
refactor(omo): deduplicate OMO/OMO Slim via OmoVariant parameterization
Introduce OmoVariant struct with STANDARD/SLIM constants to eliminate ~250 lines of copy-pasted code across DAO, service, commands, and frontend layers. Adding a new OMO variant now requires only a single const declaration instead of duplicating ~400 lines.
This commit is contained in:
+180
-271
@@ -20,15 +20,83 @@ pub struct OmoLocalFileData {
|
||||
|
||||
type OmoProfileData = (Option<Value>, Option<Value>, Option<Value>, bool);
|
||||
|
||||
// ── Variant descriptor ─────────────────────────────────────────
|
||||
|
||||
pub struct OmoVariant {
|
||||
pub filename: &'static str,
|
||||
pub category: &'static str,
|
||||
pub provider_prefix: &'static str,
|
||||
pub plugin_name: &'static str,
|
||||
pub plugin_prefix: &'static str,
|
||||
pub known_keys: &'static [&'static str],
|
||||
pub has_categories: bool,
|
||||
pub config_key: &'static str,
|
||||
pub label: &'static str,
|
||||
pub import_label: &'static str,
|
||||
}
|
||||
|
||||
pub const STANDARD: OmoVariant = OmoVariant {
|
||||
filename: "oh-my-opencode.jsonc",
|
||||
category: "omo",
|
||||
provider_prefix: "omo-",
|
||||
plugin_name: "oh-my-opencode@latest",
|
||||
plugin_prefix: "oh-my-opencode",
|
||||
known_keys: &[
|
||||
"$schema",
|
||||
"agents",
|
||||
"categories",
|
||||
"sisyphus_agent",
|
||||
"disabled_agents",
|
||||
"disabled_mcps",
|
||||
"disabled_hooks",
|
||||
"disabled_skills",
|
||||
"lsp",
|
||||
"experimental",
|
||||
"background_task",
|
||||
"browser_automation_engine",
|
||||
"claude_code",
|
||||
],
|
||||
has_categories: true,
|
||||
config_key: "common_config_omo",
|
||||
label: "OMO",
|
||||
import_label: "Imported",
|
||||
};
|
||||
|
||||
pub const SLIM: OmoVariant = OmoVariant {
|
||||
filename: "oh-my-opencode-slim.jsonc",
|
||||
category: "omo-slim",
|
||||
provider_prefix: "omo-slim-",
|
||||
plugin_name: "oh-my-opencode-slim@latest",
|
||||
plugin_prefix: "oh-my-opencode-slim",
|
||||
known_keys: &[
|
||||
"$schema",
|
||||
"agents",
|
||||
"sisyphus_agent",
|
||||
"disabled_agents",
|
||||
"disabled_mcps",
|
||||
"disabled_hooks",
|
||||
"lsp",
|
||||
"experimental",
|
||||
],
|
||||
has_categories: false,
|
||||
config_key: "common_config_omo_slim",
|
||||
label: "OMO Slim",
|
||||
import_label: "Imported Slim",
|
||||
};
|
||||
|
||||
// ── Service ────────────────────────────────────────────────────
|
||||
|
||||
pub struct OmoService;
|
||||
|
||||
impl OmoService {
|
||||
fn config_path() -> PathBuf {
|
||||
get_opencode_dir().join("oh-my-opencode.jsonc")
|
||||
// ── Path helpers ────────────────────────────────────────
|
||||
|
||||
fn config_path(v: &OmoVariant) -> PathBuf {
|
||||
get_opencode_dir().join(v.filename)
|
||||
}
|
||||
|
||||
fn resolve_local_config_path() -> Result<PathBuf, AppError> {
|
||||
let config_path = Self::config_path();
|
||||
fn resolve_local_config_path(v: &OmoVariant) -> Result<PathBuf, AppError> {
|
||||
let config_path = Self::config_path(v);
|
||||
if config_path.exists() {
|
||||
return Ok(config_path);
|
||||
}
|
||||
@@ -52,40 +120,7 @@ impl OmoService {
|
||||
.ok_or_else(|| AppError::Config("Expected JSON object".to_string()))
|
||||
}
|
||||
|
||||
const KNOWN_KEYS: [&str; 13] = [
|
||||
"$schema",
|
||||
"agents",
|
||||
"categories",
|
||||
"sisyphus_agent",
|
||||
"disabled_agents",
|
||||
"disabled_mcps",
|
||||
"disabled_hooks",
|
||||
"disabled_skills",
|
||||
"lsp",
|
||||
"experimental",
|
||||
"background_task",
|
||||
"browser_automation_engine",
|
||||
"claude_code",
|
||||
];
|
||||
|
||||
const KNOWN_KEYS_SLIM: [&str; 8] = [
|
||||
"$schema",
|
||||
"agents",
|
||||
"sisyphus_agent",
|
||||
"disabled_agents",
|
||||
"disabled_mcps",
|
||||
"disabled_hooks",
|
||||
"lsp",
|
||||
"experimental",
|
||||
];
|
||||
|
||||
fn extract_other_fields(obj: &Map<String, Value>) -> Map<String, Value> {
|
||||
Self::extract_other_fields_with_keys(obj, &Self::KNOWN_KEYS)
|
||||
}
|
||||
|
||||
fn extract_other_fields_slim(obj: &Map<String, Value>) -> Map<String, Value> {
|
||||
Self::extract_other_fields_with_keys(obj, &Self::KNOWN_KEYS_SLIM)
|
||||
}
|
||||
// ── Field extraction ───────────────────────────────────
|
||||
|
||||
fn extract_other_fields_with_keys(
|
||||
obj: &Map<String, Value>,
|
||||
@@ -141,6 +176,8 @@ impl OmoService {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Merge helpers ──────────────────────────────────────
|
||||
|
||||
fn insert_opt_value(result: &mut Map<String, Value>, key: &str, value: &Option<Value>) {
|
||||
if let Some(v) = value {
|
||||
result.insert(key.to_string(), v.clone());
|
||||
@@ -164,62 +201,40 @@ impl OmoService {
|
||||
}
|
||||
}
|
||||
|
||||
fn config_path_slim() -> PathBuf {
|
||||
get_opencode_dir().join("oh-my-opencode-slim.jsonc")
|
||||
}
|
||||
// ── Public API (variant-parameterized) ─────────────────
|
||||
|
||||
fn resolve_local_config_path_slim() -> Result<PathBuf, AppError> {
|
||||
let config_path = Self::config_path_slim();
|
||||
if config_path.exists() {
|
||||
return Ok(config_path);
|
||||
}
|
||||
|
||||
let json_path = config_path.with_extension("json");
|
||||
if json_path.exists() {
|
||||
return Ok(json_path);
|
||||
}
|
||||
|
||||
Err(AppError::OmoConfigNotFound)
|
||||
}
|
||||
|
||||
pub fn delete_config_file() -> Result<(), AppError> {
|
||||
let config_path = Self::config_path();
|
||||
pub fn delete_config_file(v: &OmoVariant) -> Result<(), AppError> {
|
||||
let config_path = Self::config_path(v);
|
||||
if config_path.exists() {
|
||||
std::fs::remove_file(&config_path).map_err(|e| AppError::io(&config_path, e))?;
|
||||
log::info!("OMO config file deleted: {config_path:?}");
|
||||
log::info!("{} config file deleted: {config_path:?}", v.label);
|
||||
}
|
||||
crate::opencode_config::remove_plugin_by_prefix("oh-my-opencode")?;
|
||||
crate::opencode_config::remove_plugin_by_prefix(v.plugin_prefix)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_config_file_slim() -> Result<(), AppError> {
|
||||
let config_path = Self::config_path_slim();
|
||||
if config_path.exists() {
|
||||
std::fs::remove_file(&config_path).map_err(|e| AppError::io(&config_path, e))?;
|
||||
log::info!("OMO Slim config file deleted: {config_path:?}");
|
||||
}
|
||||
crate::opencode_config::remove_plugin_by_prefix("oh-my-opencode-slim")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_config_to_file(state: &AppState) -> Result<(), AppError> {
|
||||
let global = state.db.get_omo_global_config()?;
|
||||
let current_omo = state.db.get_current_omo_provider("opencode")?;
|
||||
pub fn write_config_to_file(state: &AppState, v: &OmoVariant) -> Result<(), AppError> {
|
||||
let global = state.db.get_omo_global_config(v.config_key)?;
|
||||
let current_omo = state.db.get_current_omo_provider("opencode", v.category)?;
|
||||
|
||||
let profile_data = current_omo.as_ref().map(|p| {
|
||||
let agents = p.settings_config.get("agents").cloned();
|
||||
let categories = p.settings_config.get("categories").cloned();
|
||||
let categories = if v.has_categories {
|
||||
p.settings_config.get("categories").cloned()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let other_fields = p.settings_config.get("otherFields").cloned();
|
||||
let use_common_config = p
|
||||
.settings_config
|
||||
.get("useCommonConfig")
|
||||
.and_then(|v| v.as_bool())
|
||||
.and_then(|val| val.as_bool())
|
||||
.unwrap_or(true);
|
||||
(agents, categories, other_fields, use_common_config)
|
||||
});
|
||||
|
||||
let merged = Self::merge_config(&global, profile_data.as_ref());
|
||||
let config_path = Self::config_path();
|
||||
let merged = Self::merge_config(v, &global, profile_data.as_ref());
|
||||
let config_path = Self::config_path(v);
|
||||
|
||||
if let Some(parent) = config_path.parent() {
|
||||
std::fs::create_dir_all(parent).map_err(|e| AppError::io(parent, e))?;
|
||||
@@ -227,45 +242,19 @@ impl OmoService {
|
||||
|
||||
write_json_file(&config_path, &merged)?;
|
||||
|
||||
crate::opencode_config::add_plugin("oh-my-opencode@latest")?;
|
||||
crate::opencode_config::add_plugin(v.plugin_name)?;
|
||||
|
||||
log::info!("OMO config written to {config_path:?}");
|
||||
log::info!("{} config written to {config_path:?}", v.label);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_config_to_file_slim(state: &AppState) -> Result<(), AppError> {
|
||||
let global = state.db.get_omo_slim_global_config()?;
|
||||
let current_omo = state.db.get_current_omo_slim_provider("opencode")?;
|
||||
|
||||
let profile_data = current_omo.as_ref().map(|p| {
|
||||
let agents = p.settings_config.get("agents").cloned();
|
||||
let other_fields = p.settings_config.get("otherFields").cloned();
|
||||
let use_common_config = p
|
||||
.settings_config
|
||||
.get("useCommonConfig")
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(true);
|
||||
(agents, None, other_fields, use_common_config)
|
||||
});
|
||||
|
||||
let merged = Self::merge_config_slim(&global, profile_data.as_ref());
|
||||
let config_path = Self::config_path_slim();
|
||||
|
||||
if let Some(parent) = config_path.parent() {
|
||||
std::fs::create_dir_all(parent).map_err(|e| AppError::io(parent, e))?;
|
||||
}
|
||||
|
||||
write_json_file(&config_path, &merged)?;
|
||||
|
||||
crate::opencode_config::add_plugin("oh-my-opencode-slim@latest")?;
|
||||
|
||||
log::info!("OMO Slim config written to {config_path:?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn merge_config(global: &OmoGlobalConfig, profile_data: Option<&OmoProfileData>) -> Value {
|
||||
fn merge_config(
|
||||
v: &OmoVariant,
|
||||
global: &OmoGlobalConfig,
|
||||
profile_data: Option<&OmoProfileData>,
|
||||
) -> Value {
|
||||
let mut result = Map::new();
|
||||
let use_common_config = profile_data.map(|(_, _, _, v)| *v).unwrap_or(true);
|
||||
let use_common_config = profile_data.map(|(_, _, _, uc)| *uc).unwrap_or(true);
|
||||
|
||||
if use_common_config {
|
||||
if let Some(url) = &global.schema_url {
|
||||
@@ -276,141 +265,67 @@ impl OmoService {
|
||||
Self::insert_string_array(&mut result, "disabled_agents", &global.disabled_agents);
|
||||
Self::insert_string_array(&mut result, "disabled_mcps", &global.disabled_mcps);
|
||||
Self::insert_string_array(&mut result, "disabled_hooks", &global.disabled_hooks);
|
||||
Self::insert_string_array(&mut result, "disabled_skills", &global.disabled_skills);
|
||||
|
||||
if v.has_categories {
|
||||
Self::insert_string_array(&mut result, "disabled_skills", &global.disabled_skills);
|
||||
Self::insert_opt_value(&mut result, "background_task", &global.background_task);
|
||||
Self::insert_opt_value(
|
||||
&mut result,
|
||||
"browser_automation_engine",
|
||||
&global.browser_automation_engine,
|
||||
);
|
||||
Self::insert_opt_value(&mut result, "claude_code", &global.claude_code);
|
||||
}
|
||||
|
||||
Self::insert_opt_value(&mut result, "lsp", &global.lsp);
|
||||
Self::insert_opt_value(&mut result, "experimental", &global.experimental);
|
||||
Self::insert_opt_value(&mut result, "background_task", &global.background_task);
|
||||
Self::insert_opt_value(
|
||||
&mut result,
|
||||
"browser_automation_engine",
|
||||
&global.browser_automation_engine,
|
||||
);
|
||||
Self::insert_opt_value(&mut result, "claude_code", &global.claude_code);
|
||||
|
||||
Self::insert_object_entries(&mut result, global.other_fields.as_ref());
|
||||
}
|
||||
|
||||
if let Some((agents, categories, other_fields, _)) = profile_data {
|
||||
Self::insert_opt_value(&mut result, "agents", agents);
|
||||
Self::insert_opt_value(&mut result, "categories", categories);
|
||||
Self::insert_object_entries(&mut result, other_fields.as_ref());
|
||||
}
|
||||
|
||||
Value::Object(result)
|
||||
}
|
||||
|
||||
/// Merge config for Slim variant (no categories, no disabled_skills, no background_task, etc.)
|
||||
fn merge_config_slim(global: &OmoGlobalConfig, profile_data: Option<&OmoProfileData>) -> Value {
|
||||
let mut result = Map::new();
|
||||
let use_common_config = profile_data.map(|(_, _, _, v)| *v).unwrap_or(true);
|
||||
|
||||
if use_common_config {
|
||||
if let Some(url) = &global.schema_url {
|
||||
result.insert("$schema".to_string(), Value::String(url.clone()));
|
||||
if v.has_categories {
|
||||
Self::insert_opt_value(&mut result, "categories", categories);
|
||||
}
|
||||
|
||||
Self::insert_opt_value(&mut result, "sisyphus_agent", &global.sisyphus_agent);
|
||||
Self::insert_string_array(&mut result, "disabled_agents", &global.disabled_agents);
|
||||
Self::insert_string_array(&mut result, "disabled_mcps", &global.disabled_mcps);
|
||||
Self::insert_string_array(&mut result, "disabled_hooks", &global.disabled_hooks);
|
||||
// Slim does NOT support: disabled_skills, background_task, browser_automation_engine, claude_code
|
||||
Self::insert_opt_value(&mut result, "lsp", &global.lsp);
|
||||
Self::insert_opt_value(&mut result, "experimental", &global.experimental);
|
||||
|
||||
Self::insert_object_entries(&mut result, global.other_fields.as_ref());
|
||||
}
|
||||
|
||||
if let Some((agents, _categories, other_fields, _)) = profile_data {
|
||||
Self::insert_opt_value(&mut result, "agents", agents);
|
||||
// Slim does NOT have categories
|
||||
Self::insert_object_entries(&mut result, other_fields.as_ref());
|
||||
}
|
||||
|
||||
Value::Object(result)
|
||||
}
|
||||
|
||||
pub fn import_from_local(state: &AppState) -> Result<crate::provider::Provider, AppError> {
|
||||
let actual_path = Self::resolve_local_config_path()?;
|
||||
Self::import_from_path(state, &actual_path)
|
||||
}
|
||||
|
||||
fn import_from_path(
|
||||
pub fn import_from_local(
|
||||
state: &AppState,
|
||||
path: &std::path::Path,
|
||||
v: &OmoVariant,
|
||||
) -> Result<crate::provider::Provider, AppError> {
|
||||
let obj = Self::read_jsonc_object(path)?;
|
||||
|
||||
let mut settings = Map::new();
|
||||
if let Some(agents) = obj.get("agents") {
|
||||
settings.insert("agents".to_string(), agents.clone());
|
||||
}
|
||||
if let Some(categories) = obj.get("categories") {
|
||||
settings.insert("categories".to_string(), categories.clone());
|
||||
}
|
||||
settings.insert("useCommonConfig".to_string(), Value::Bool(true));
|
||||
|
||||
let other = Self::extract_other_fields(&obj);
|
||||
if !other.is_empty() {
|
||||
settings.insert("otherFields".to_string(), Value::Object(other));
|
||||
}
|
||||
|
||||
let mut global = state.db.get_omo_global_config()?;
|
||||
Self::merge_global_from_obj(&obj, &mut global);
|
||||
global.updated_at = chrono::Utc::now().to_rfc3339();
|
||||
state.db.save_omo_global_config(&global)?;
|
||||
|
||||
let provider_id = format!("omo-{}", uuid::Uuid::new_v4());
|
||||
let name = format!("Imported {}", chrono::Local::now().format("%Y-%m-%d %H:%M"));
|
||||
let settings_config =
|
||||
serde_json::to_value(&settings).unwrap_or_else(|_| serde_json::json!({}));
|
||||
|
||||
let provider = crate::provider::Provider {
|
||||
id: provider_id,
|
||||
name,
|
||||
settings_config,
|
||||
website_url: None,
|
||||
category: Some("omo".to_string()),
|
||||
created_at: Some(chrono::Utc::now().timestamp_millis()),
|
||||
sort_index: None,
|
||||
notes: None,
|
||||
meta: None,
|
||||
icon: None,
|
||||
icon_color: None,
|
||||
in_failover_queue: false,
|
||||
};
|
||||
|
||||
state.db.save_provider("opencode", &provider)?;
|
||||
state
|
||||
.db
|
||||
.set_omo_provider_current("opencode", &provider.id)?;
|
||||
Self::write_config_to_file(state)?;
|
||||
Ok(provider)
|
||||
}
|
||||
|
||||
pub fn import_from_local_slim(state: &AppState) -> Result<crate::provider::Provider, AppError> {
|
||||
let actual_path = Self::resolve_local_config_path_slim()?;
|
||||
let actual_path = Self::resolve_local_config_path(v)?;
|
||||
let obj = Self::read_jsonc_object(&actual_path)?;
|
||||
|
||||
let mut settings = Map::new();
|
||||
if let Some(agents) = obj.get("agents") {
|
||||
settings.insert("agents".to_string(), agents.clone());
|
||||
}
|
||||
// Slim has no categories
|
||||
if v.has_categories {
|
||||
if let Some(categories) = obj.get("categories") {
|
||||
settings.insert("categories".to_string(), categories.clone());
|
||||
}
|
||||
}
|
||||
settings.insert("useCommonConfig".to_string(), Value::Bool(true));
|
||||
|
||||
let other = Self::extract_other_fields_slim(&obj);
|
||||
let other = Self::extract_other_fields_with_keys(&obj, v.known_keys);
|
||||
if !other.is_empty() {
|
||||
settings.insert("otherFields".to_string(), Value::Object(other));
|
||||
}
|
||||
|
||||
let mut global = state.db.get_omo_slim_global_config()?;
|
||||
let mut global = state.db.get_omo_global_config(v.config_key)?;
|
||||
Self::merge_global_from_obj(&obj, &mut global);
|
||||
global.updated_at = chrono::Utc::now().to_rfc3339();
|
||||
state.db.save_omo_slim_global_config(&global)?;
|
||||
state.db.save_omo_global_config(v.config_key, &global)?;
|
||||
|
||||
let provider_id = format!("omo-slim-{}", uuid::Uuid::new_v4());
|
||||
let provider_id = format!("{}{}", v.provider_prefix, uuid::Uuid::new_v4());
|
||||
let name = format!(
|
||||
"Imported Slim {}",
|
||||
"{} {}",
|
||||
v.import_label,
|
||||
chrono::Local::now().format("%Y-%m-%d %H:%M")
|
||||
);
|
||||
let settings_config =
|
||||
@@ -421,7 +336,7 @@ impl OmoService {
|
||||
name,
|
||||
settings_config,
|
||||
website_url: None,
|
||||
category: Some("omo-slim".to_string()),
|
||||
category: Some(v.category.to_string()),
|
||||
created_at: Some(chrono::Utc::now().timestamp_millis()),
|
||||
sort_index: None,
|
||||
notes: None,
|
||||
@@ -434,13 +349,13 @@ impl OmoService {
|
||||
state.db.save_provider("opencode", &provider)?;
|
||||
state
|
||||
.db
|
||||
.set_omo_slim_provider_current("opencode", &provider.id)?;
|
||||
Self::write_config_to_file_slim(state)?;
|
||||
.set_omo_provider_current("opencode", &provider.id, v.category)?;
|
||||
Self::write_config_to_file(state, v)?;
|
||||
Ok(provider)
|
||||
}
|
||||
|
||||
pub fn read_local_file() -> Result<OmoLocalFileData, AppError> {
|
||||
let actual_path = Self::resolve_local_config_path()?;
|
||||
pub fn read_local_file(v: &OmoVariant) -> Result<OmoLocalFileData, AppError> {
|
||||
let actual_path = Self::resolve_local_config_path(v)?;
|
||||
let metadata = std::fs::metadata(&actual_path).ok();
|
||||
let last_modified = metadata
|
||||
.and_then(|m| m.modified().ok())
|
||||
@@ -448,38 +363,28 @@ impl OmoService {
|
||||
|
||||
let obj = Self::read_jsonc_object(&actual_path)?;
|
||||
|
||||
Ok(Self::build_local_file_data_from_obj(
|
||||
Ok(Self::build_local_file_data(
|
||||
v,
|
||||
&obj,
|
||||
actual_path.to_string_lossy().to_string(),
|
||||
last_modified,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn read_local_file_slim() -> Result<OmoLocalFileData, AppError> {
|
||||
let actual_path = Self::resolve_local_config_path_slim()?;
|
||||
let metadata = std::fs::metadata(&actual_path).ok();
|
||||
let last_modified = metadata
|
||||
.and_then(|m| m.modified().ok())
|
||||
.map(|t| chrono::DateTime::<chrono::Utc>::from(t).to_rfc3339());
|
||||
|
||||
let obj = Self::read_jsonc_object(&actual_path)?;
|
||||
|
||||
Ok(Self::build_local_file_data_slim_from_obj(
|
||||
&obj,
|
||||
actual_path.to_string_lossy().to_string(),
|
||||
last_modified,
|
||||
))
|
||||
}
|
||||
|
||||
fn build_local_file_data_from_obj(
|
||||
fn build_local_file_data(
|
||||
v: &OmoVariant,
|
||||
obj: &Map<String, Value>,
|
||||
file_path: String,
|
||||
last_modified: Option<String>,
|
||||
) -> OmoLocalFileData {
|
||||
let agents = obj.get("agents").cloned();
|
||||
let categories = obj.get("categories").cloned();
|
||||
let categories = if v.has_categories {
|
||||
obj.get("categories").cloned()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let other = Self::extract_other_fields(obj);
|
||||
let other = Self::extract_other_fields_with_keys(obj, v.known_keys);
|
||||
let other_fields = if other.is_empty() {
|
||||
None
|
||||
} else {
|
||||
@@ -500,35 +405,6 @@ impl OmoService {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_local_file_data_slim_from_obj(
|
||||
obj: &Map<String, Value>,
|
||||
file_path: String,
|
||||
last_modified: Option<String>,
|
||||
) -> OmoLocalFileData {
|
||||
let agents = obj.get("agents").cloned();
|
||||
// Slim has no categories
|
||||
|
||||
let other = Self::extract_other_fields_slim(obj);
|
||||
let other_fields = if other.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Value::Object(other))
|
||||
};
|
||||
|
||||
let mut global = OmoGlobalConfig::default();
|
||||
Self::merge_global_from_obj(obj, &mut global);
|
||||
global.other_fields = other_fields.clone();
|
||||
|
||||
OmoLocalFileData {
|
||||
agents,
|
||||
categories: None,
|
||||
other_fields,
|
||||
global,
|
||||
file_path,
|
||||
last_modified,
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_jsonc_comments(input: &str) -> String {
|
||||
let mut result = String::with_capacity(input.len());
|
||||
let mut chars = input.chars().peekable();
|
||||
@@ -608,7 +484,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_merge_config_empty() {
|
||||
let global = OmoGlobalConfig::default();
|
||||
let merged = OmoService::merge_config(&global, None);
|
||||
let merged = OmoService::merge_config(&STANDARD, &global, None);
|
||||
assert!(merged.is_object());
|
||||
}
|
||||
|
||||
@@ -625,7 +501,7 @@ mod tests {
|
||||
let categories = None;
|
||||
let other_fields = None;
|
||||
let profile_data = (agents, categories, other_fields, true);
|
||||
let merged = OmoService::merge_config(&global, Some(&profile_data));
|
||||
let merged = OmoService::merge_config(&STANDARD, &global, Some(&profile_data));
|
||||
let obj = merged.as_object().unwrap();
|
||||
|
||||
assert_eq!(obj["$schema"], "https://example.com/schema.json");
|
||||
@@ -647,7 +523,7 @@ mod tests {
|
||||
let categories = None;
|
||||
let other_fields = None;
|
||||
let profile_data = (agents, categories, other_fields, false);
|
||||
let merged = OmoService::merge_config(&global, Some(&profile_data));
|
||||
let merged = OmoService::merge_config(&STANDARD, &global, Some(&profile_data));
|
||||
let obj = merged.as_object().unwrap();
|
||||
|
||||
assert!(!obj.contains_key("$schema"));
|
||||
@@ -672,7 +548,8 @@ mod tests {
|
||||
});
|
||||
let obj_map = obj.as_object().unwrap().clone();
|
||||
|
||||
let data = OmoService::build_local_file_data_from_obj(
|
||||
let data = OmoService::build_local_file_data(
|
||||
&STANDARD,
|
||||
&obj_map,
|
||||
"/tmp/oh-my-opencode.jsonc".to_string(),
|
||||
None,
|
||||
@@ -704,11 +581,43 @@ mod tests {
|
||||
let other_fields = Some(serde_json::json!("profile_non_object"));
|
||||
let profile_data = (agents, categories, other_fields, true);
|
||||
|
||||
let merged = OmoService::merge_config(&global, Some(&profile_data));
|
||||
let merged = OmoService::merge_config(&STANDARD, &global, Some(&profile_data));
|
||||
let obj = merged.as_object().unwrap();
|
||||
|
||||
assert!(!obj.contains_key("0"));
|
||||
assert!(!obj.contains_key("global_non_object"));
|
||||
assert!(!obj.contains_key("profile_non_object"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merge_config_slim_excludes_categories_and_extra_fields() {
|
||||
let global = OmoGlobalConfig {
|
||||
schema_url: Some("https://slim.schema".to_string()),
|
||||
disabled_agents: vec!["oracle".to_string()],
|
||||
disabled_skills: vec!["playwright".to_string()],
|
||||
background_task: Some(serde_json::json!({"key": "val"})),
|
||||
browser_automation_engine: Some(serde_json::json!({"provider": "pw"})),
|
||||
claude_code: Some(serde_json::json!({"mcp": true})),
|
||||
..Default::default()
|
||||
};
|
||||
let agents = Some(serde_json::json!({"orchestrator": {"model": "k2"}}));
|
||||
let categories = Some(serde_json::json!({"code": {"model": "gpt"}}));
|
||||
let other_fields = None;
|
||||
let profile_data = (agents, categories, other_fields, true);
|
||||
|
||||
let merged = OmoService::merge_config(&SLIM, &global, Some(&profile_data));
|
||||
let obj = merged.as_object().unwrap();
|
||||
|
||||
// Slim should NOT include these
|
||||
assert!(!obj.contains_key("disabled_skills"));
|
||||
assert!(!obj.contains_key("background_task"));
|
||||
assert!(!obj.contains_key("browser_automation_engine"));
|
||||
assert!(!obj.contains_key("claude_code"));
|
||||
assert!(!obj.contains_key("categories"));
|
||||
|
||||
// Slim SHOULD include these
|
||||
assert_eq!(obj["$schema"], "https://slim.schema");
|
||||
assert!(obj.contains_key("agents"));
|
||||
assert!(obj.contains_key("disabled_agents"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,22 +208,31 @@ impl ProviderService {
|
||||
if app_type.is_additive_mode() {
|
||||
if matches!(app_type, AppType::OpenCode) && provider.category.as_deref() == Some("omo")
|
||||
{
|
||||
let is_omo_current = state
|
||||
.db
|
||||
.is_omo_provider_current(app_type.as_str(), &provider.id)?;
|
||||
let is_omo_current =
|
||||
state
|
||||
.db
|
||||
.is_omo_provider_current(app_type.as_str(), &provider.id, "omo")?;
|
||||
if is_omo_current {
|
||||
crate::services::OmoService::write_config_to_file(state)?;
|
||||
crate::services::OmoService::write_config_to_file(
|
||||
state,
|
||||
&crate::services::omo::STANDARD,
|
||||
)?;
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
if matches!(app_type, AppType::OpenCode)
|
||||
&& provider.category.as_deref() == Some("omo-slim")
|
||||
{
|
||||
let is_current = state
|
||||
.db
|
||||
.is_omo_slim_provider_current(app_type.as_str(), &provider.id)?;
|
||||
let is_current = state.db.is_omo_provider_current(
|
||||
app_type.as_str(),
|
||||
&provider.id,
|
||||
"omo-slim",
|
||||
)?;
|
||||
if is_current {
|
||||
crate::services::OmoService::write_config_to_file_slim(state)?;
|
||||
crate::services::OmoService::write_config_to_file(
|
||||
state,
|
||||
&crate::services::omo::SLIM,
|
||||
)?;
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
@@ -279,7 +288,10 @@ impl ProviderService {
|
||||
.and_then(|p| p.category);
|
||||
|
||||
if provider_category.as_deref() == Some("omo") {
|
||||
let was_current = state.db.is_omo_provider_current(app_type.as_str(), id)?;
|
||||
let was_current =
|
||||
state
|
||||
.db
|
||||
.is_omo_provider_current(app_type.as_str(), id, "omo")?;
|
||||
let omo_count = state
|
||||
.db
|
||||
.get_all_providers(app_type.as_str())?
|
||||
@@ -295,15 +307,18 @@ impl ProviderService {
|
||||
|
||||
state.db.delete_provider(app_type.as_str(), id)?;
|
||||
if was_current {
|
||||
crate::services::OmoService::delete_config_file()?;
|
||||
crate::services::OmoService::delete_config_file(
|
||||
&crate::services::omo::STANDARD,
|
||||
)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if provider_category.as_deref() == Some("omo-slim") {
|
||||
let was_current = state
|
||||
.db
|
||||
.is_omo_slim_provider_current(app_type.as_str(), id)?;
|
||||
let was_current =
|
||||
state
|
||||
.db
|
||||
.is_omo_provider_current(app_type.as_str(), id, "omo-slim")?;
|
||||
let slim_count = state
|
||||
.db
|
||||
.get_all_providers(app_type.as_str())?
|
||||
@@ -319,7 +334,9 @@ impl ProviderService {
|
||||
|
||||
state.db.delete_provider(app_type.as_str(), id)?;
|
||||
if was_current {
|
||||
crate::services::OmoService::delete_config_file_slim()?;
|
||||
crate::services::OmoService::delete_config_file(
|
||||
&crate::services::omo::SLIM,
|
||||
)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
@@ -366,26 +383,40 @@ impl ProviderService {
|
||||
.and_then(|p| p.category);
|
||||
|
||||
if provider_category.as_deref() == Some("omo") {
|
||||
state.db.clear_omo_provider_current(app_type.as_str(), id)?;
|
||||
let still_has_current =
|
||||
state.db.get_current_omo_provider("opencode")?.is_some();
|
||||
state
|
||||
.db
|
||||
.clear_omo_provider_current(app_type.as_str(), id, "omo")?;
|
||||
let still_has_current = state
|
||||
.db
|
||||
.get_current_omo_provider("opencode", "omo")?
|
||||
.is_some();
|
||||
if still_has_current {
|
||||
crate::services::OmoService::write_config_to_file(state)?;
|
||||
crate::services::OmoService::write_config_to_file(
|
||||
state,
|
||||
&crate::services::omo::STANDARD,
|
||||
)?;
|
||||
} else {
|
||||
crate::services::OmoService::delete_config_file()?;
|
||||
crate::services::OmoService::delete_config_file(
|
||||
&crate::services::omo::STANDARD,
|
||||
)?;
|
||||
}
|
||||
} else if provider_category.as_deref() == Some("omo-slim") {
|
||||
state
|
||||
.db
|
||||
.clear_omo_slim_provider_current(app_type.as_str(), id)?;
|
||||
.clear_omo_provider_current(app_type.as_str(), id, "omo-slim")?;
|
||||
let still_has_current = state
|
||||
.db
|
||||
.get_current_omo_slim_provider("opencode")?
|
||||
.get_current_omo_provider("opencode", "omo-slim")?
|
||||
.is_some();
|
||||
if still_has_current {
|
||||
crate::services::OmoService::write_config_to_file_slim(state)?;
|
||||
crate::services::OmoService::write_config_to_file(
|
||||
state,
|
||||
&crate::services::omo::SLIM,
|
||||
)?;
|
||||
} else {
|
||||
crate::services::OmoService::delete_config_file_slim()?;
|
||||
crate::services::OmoService::delete_config_file(
|
||||
&crate::services::omo::SLIM,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
remove_opencode_provider_from_live(id)?;
|
||||
@@ -507,8 +538,13 @@ impl ProviderService {
|
||||
.ok_or_else(|| AppError::Message(format!("供应商 {id} 不存在")))?;
|
||||
|
||||
if matches!(app_type, AppType::OpenCode) && provider.category.as_deref() == Some("omo") {
|
||||
state.db.set_omo_provider_current(app_type.as_str(), id)?;
|
||||
crate::services::OmoService::write_config_to_file(state)?;
|
||||
state
|
||||
.db
|
||||
.set_omo_provider_current(app_type.as_str(), id, "omo")?;
|
||||
crate::services::OmoService::write_config_to_file(
|
||||
state,
|
||||
&crate::services::omo::STANDARD,
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -516,8 +552,8 @@ impl ProviderService {
|
||||
{
|
||||
state
|
||||
.db
|
||||
.set_omo_slim_provider_current(app_type.as_str(), id)?;
|
||||
crate::services::OmoService::write_config_to_file_slim(state)?;
|
||||
.set_omo_provider_current(app_type.as_str(), id, "omo-slim")?;
|
||||
crate::services::OmoService::write_config_to_file(state, &crate::services::omo::SLIM)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user