rsclaw 0.0.1-alpha.1

rsclaw: High-performance AI agent (BETA). Optimized for M4 Max and 2GB VPS. 100% compatible with openclaw
Documentation
use serde::{Deserialize, Serialize};
use serde_json::Value;

/// OpenClaw compatible configuration - allows unknown fields for forward compatibility.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OpenClawConfig {
    pub meta: Option<Value>,
    pub wizard: Option<Value>,
    pub gateway: Option<Value>,
    pub agents: Option<Value>,
    pub models: Option<Value>,
    pub auth: Option<Value>,
    pub channels: Option<Value>,
    pub session: Option<Value>,
    pub bindings: Option<Value>,
    pub cron: Option<Value>,
    pub tools: Option<Value>,
    pub sandbox: Option<Value>,
    pub skills: Option<Value>,
    pub plugins: Option<Value>,
    pub hooks: Option<Value>,
    pub memory: Option<Value>,
    pub messages: Option<Value>,
    pub commands: Option<Value>,
}

impl OpenClawConfig {
    /// Parse from JSON string.
    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
        serde_json::from_str(json)
    }

    /// Get gateway host.
    pub fn gateway_host(&self) -> String {
        self.gateway
            .as_ref()
            .and_then(|g| g.get("bind"))
            .and_then(|b| b.as_str())
            .map(|s| {
                if s == "loopback" {
                    "127.0.0.1"
                } else {
                    "0.0.0.0"
                }
            })
            .unwrap_or("127.0.0.1")
            .to_string()
    }

    /// Get gateway port.
    pub fn gateway_port(&self) -> u16 {
        self.gateway
            .as_ref()
            .and_then(|g| g.get("port"))
            .and_then(|p| p.as_u64())
            .unwrap_or(8080) as u16
    }

    /// Get providers as vector.
    pub fn get_providers(&self) -> Vec<ProviderEntry> {
        let mut providers = Vec::new();

        if let Some(models) = &self.models {
            if let Some(providers_obj) = models.get("providers") {
                if let Some(map) = providers_obj.as_object() {
                    for (name, config) in map {
                        let provider_type = config
                            .get("api")
                            .and_then(|a| a.as_str())
                            .unwrap_or("openai");

                        // Normalize provider type
                        let provider_type = if provider_type.starts_with("openai") {
                            "openai"
                        } else {
                            provider_type
                        };

                        let base_url = config
                            .get("baseUrl")
                            .and_then(|u| u.as_str())
                            .map(|s| s.to_string());

                        let api_key = config
                            .get("apiKey")
                            .and_then(|k| k.as_str())
                            .map(|s| s.to_string());

                        let mut models_list = Vec::new();
                        if let Some(models_arr) = config.get("models").and_then(|m| m.as_array()) {
                            for model in models_arr {
                                if let Some(id) = model.get("id").and_then(|i| i.as_str()) {
                                    let max_tokens = model
                                        .get("maxTokens")
                                        .and_then(|t| t.as_u64())
                                        .map(|t| t as u32);

                                    models_list.push(ModelEntry {
                                        name: id.to_string(),
                                        max_tokens,
                                        supports_functions: None,
                                        supports_vision: None,
                                    });
                                }
                            }
                        }

                        providers.push(ProviderEntry {
                            name: name.clone(),
                            provider_type: provider_type.to_string(),
                            api_key,
                            base_url,
                            models: models_list,
                        });
                    }
                }
            }
        }

        providers
    }

    /// Get agents list.
    pub fn get_agents(&self) -> Vec<AgentEntry> {
        let mut agents = Vec::new();

        if let Some(agents_config) = &self.agents {
            if let Some(defaults) = agents_config.get("defaults") {
                let model = defaults
                    .get("model")
                    .and_then(|m| m.get("primary"))
                    .and_then(|p| p.as_str())
                    .map(|s| s.to_string());

                let _workspace = defaults
                    .get("workspace")
                    .and_then(|w| w.as_str())
                    .map(|s| s.to_string());

                let max_concurrent = defaults
                    .get("maxConcurrent")
                    .and_then(|c| c.as_u64())
                    .map(|c| c as u32);

                agents.push(AgentEntry {
                    name: "default".to_string(),
                    model,
                    system_prompt: None,
                    tools: None,
                    channels: None,
                    default: true,
                    max_tokens: None,
                    memory_limit_mb: max_concurrent.map(|c| c * 512),
                });
            }
        }

        agents
    }
}

/// Provider entry for runtime config.
#[derive(Debug, Clone)]
pub struct ProviderEntry {
    pub name: String,
    pub provider_type: String,
    pub api_key: Option<String>,
    pub base_url: Option<String>,
    pub models: Vec<ModelEntry>,
}

/// Model entry.
#[derive(Debug, Clone)]
pub struct ModelEntry {
    pub name: String,
    pub max_tokens: Option<u32>,
    pub supports_functions: Option<bool>,
    pub supports_vision: Option<bool>,
}

/// Agent entry.
#[derive(Debug, Clone)]
pub struct AgentEntry {
    pub name: String,
    pub model: Option<String>,
    pub system_prompt: Option<String>,
    pub tools: Option<Vec<String>>,
    pub channels: Option<Vec<String>>,
    pub default: bool,
    pub max_tokens: Option<u32>,
    pub memory_limit_mb: Option<u32>,
}