mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
feat: add Hermes UI components, presets, and config panels (Phase 8)
- Add 7 provider presets (OpenRouter, Anthropic, OpenAI, Google, DeepSeek, Together, Nous) - Create HermesFormFields + useHermesFormState for provider form integration - Create Model/Agent/Env config panels with save/load functionality - Create HermesHealthBanner for config warnings - Add hermes icon (violet winged H) to icon system - Integrate into App.tsx: 3 new view types (hermesModel/hermesAgent/hermesEnv), sidebar buttons (Brain/Bot/KeyRound), health banner, session support - Integrate into ProviderForm: presets, form state, key validation, rendering - Integrate into AddProviderDialog: universal tab exclusion, providerKey, base_url extraction - Add i18n keys for all Hermes UI (zh/en/ja)
This commit is contained in:
@@ -171,9 +171,7 @@ fn is_top_level_key_line(line: &str) -> bool {
|
||||
}
|
||||
if let Some(colon_pos) = line.find(':') {
|
||||
let after_colon = &line[colon_pos + 1..];
|
||||
after_colon.is_empty()
|
||||
|| after_colon.starts_with(' ')
|
||||
|| after_colon.starts_with('\t')
|
||||
after_colon.is_empty() || after_colon.starts_with(' ') || after_colon.starts_with('\t')
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -221,10 +219,7 @@ fn find_yaml_section_range(raw: &str, section_key: &str) -> Option<(usize, usize
|
||||
/// ```
|
||||
fn serialize_yaml_section(key: &str, value: &serde_yaml::Value) -> Result<String, AppError> {
|
||||
let mut section = serde_yaml::Mapping::new();
|
||||
section.insert(
|
||||
serde_yaml::Value::String(key.to_string()),
|
||||
value.clone(),
|
||||
);
|
||||
section.insert(serde_yaml::Value::String(key.to_string()), value.clone());
|
||||
let yaml_str = serde_yaml::to_string(&serde_yaml::Value::Mapping(section))
|
||||
.map_err(|e| AppError::Config(format!("Failed to serialize YAML section '{key}': {e}")))?;
|
||||
Ok(yaml_str)
|
||||
@@ -367,7 +362,10 @@ fn write_yaml_section_to_config_locked(
|
||||
|
||||
let warnings = scan_hermes_health_internal(&new_raw);
|
||||
|
||||
log::debug!("Hermes config section '{section_key}' written to {:?}", config_path);
|
||||
log::debug!(
|
||||
"Hermes config section '{section_key}' written to {:?}",
|
||||
config_path
|
||||
);
|
||||
Ok(HermesWriteOutcome {
|
||||
backup_path: backup_path.map(|p| p.display().to_string()),
|
||||
warnings,
|
||||
@@ -438,11 +436,10 @@ pub fn set_provider(
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(existing) = providers.iter_mut().find(|p| {
|
||||
p.get("name")
|
||||
.and_then(|n| n.as_str())
|
||||
== Some(name)
|
||||
}) {
|
||||
if let Some(existing) = providers
|
||||
.iter_mut()
|
||||
.find(|p| p.get("name").and_then(|n| n.as_str()) == Some(name))
|
||||
{
|
||||
*existing = yaml_val;
|
||||
} else {
|
||||
providers.push(yaml_val);
|
||||
@@ -467,11 +464,7 @@ pub fn remove_provider(name: &str) -> Result<HermesWriteOutcome, AppError> {
|
||||
.unwrap_or_default();
|
||||
|
||||
let original_len = providers.len();
|
||||
providers.retain(|p| {
|
||||
p.get("name")
|
||||
.and_then(|n| n.as_str())
|
||||
!= Some(name)
|
||||
});
|
||||
providers.retain(|p| p.get("name").and_then(|n| n.as_str()) != Some(name));
|
||||
|
||||
if providers.len() == original_len {
|
||||
return Ok(HermesWriteOutcome::default());
|
||||
@@ -690,9 +683,8 @@ fn scan_hermes_health_internal(content: &str) -> Vec<HermesHealthWarning> {
|
||||
if !providers.is_sequence() {
|
||||
warnings.push(HermesHealthWarning {
|
||||
code: "custom_providers_not_list".to_string(),
|
||||
message:
|
||||
"custom_providers should be a YAML list (sequence), not a mapping"
|
||||
.to_string(),
|
||||
message: "custom_providers should be a YAML list (sequence), not a mapping"
|
||||
.to_string(),
|
||||
path: Some("custom_providers".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -83,8 +83,7 @@ fn convert_to_hermes_format(spec: &Value) -> Result<Value, AppError> {
|
||||
result.insert("url".into(), url.clone());
|
||||
}
|
||||
if let Some(headers) = obj.get("headers") {
|
||||
if headers.is_object()
|
||||
&& !headers.as_object().map(|o| o.is_empty()).unwrap_or(true)
|
||||
if headers.is_object() && !headers.as_object().map(|o| o.is_empty()).unwrap_or(true)
|
||||
{
|
||||
result.insert("headers".into(), headers.clone());
|
||||
}
|
||||
@@ -142,9 +141,7 @@ fn convert_from_hermes_format(id: &str, spec: &Value) -> Result<Value, AppError>
|
||||
result.insert("url".into(), url.clone());
|
||||
}
|
||||
if let Some(headers) = obj.get("headers") {
|
||||
if headers.is_object()
|
||||
&& !headers.as_object().map(|o| o.is_empty()).unwrap_or(true)
|
||||
{
|
||||
if headers.is_object() && !headers.as_object().map(|o| o.is_empty()).unwrap_or(true) {
|
||||
result.insert("headers".into(), headers.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,11 +132,7 @@ impl McpService {
|
||||
log::debug!("OpenClaw MCP support is still in development, skipping sync");
|
||||
}
|
||||
AppType::Hermes => {
|
||||
mcp::sync_single_server_to_hermes(
|
||||
&Default::default(),
|
||||
&server.id,
|
||||
&server.server,
|
||||
)?;
|
||||
mcp::sync_single_server_to_hermes(&Default::default(), &server.id, &server.server)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1393,9 +1393,7 @@ pub fn remove_hermes_provider_from_live(provider_id: &str) -> Result<(), AppErro
|
||||
|
||||
// Check if Hermes config directory exists
|
||||
if !hermes_config::get_hermes_dir().exists() {
|
||||
log::debug!(
|
||||
"Hermes config directory doesn't exist, skipping removal of '{provider_id}'"
|
||||
);
|
||||
log::debug!("Hermes config directory doesn't exist, skipping removal of '{provider_id}'");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,8 @@ use crate::store::AppState;
|
||||
|
||||
// Re-export sub-module functions for external access
|
||||
pub use live::{
|
||||
import_default_config, import_hermes_providers_from_live,
|
||||
import_openclaw_providers_from_live, import_opencode_providers_from_live, read_live_settings,
|
||||
sync_current_to_live,
|
||||
import_default_config, import_hermes_providers_from_live, import_openclaw_providers_from_live,
|
||||
import_opencode_providers_from_live, read_live_settings, sync_current_to_live,
|
||||
};
|
||||
|
||||
// Internal re-exports (pub(crate))
|
||||
|
||||
Reference in New Issue
Block a user