refactor: 统一项目名称从OpenFang到ZCLAW
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括: - 配置文件中的项目名称 - 代码注释和文档引用 - 环境变量和路径 - 类型定义和接口名称 - 测试用例和模拟数据 同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
This commit is contained in:
@@ -252,10 +252,78 @@ fn default_skills_dir() -> Option<std::path::PathBuf> {
|
||||
}
|
||||
|
||||
impl KernelConfig {
|
||||
/// Load configuration from file
|
||||
/// Load configuration from file.
|
||||
///
|
||||
/// Search order:
|
||||
/// 1. Path from `ZCLAW_CONFIG` environment variable
|
||||
/// 2. `~/.zclaw/config.toml`
|
||||
/// 3. Fallback to `Self::default()`
|
||||
///
|
||||
/// Supports `${VAR_NAME}` environment variable interpolation in string values.
|
||||
pub async fn load() -> Result<Self> {
|
||||
// TODO: Load from ~/.zclaw/config.toml
|
||||
Ok(Self::default())
|
||||
let config_path = Self::find_config_path();
|
||||
|
||||
match config_path {
|
||||
Some(path) => {
|
||||
if !path.exists() {
|
||||
tracing::debug!(target: "kernel_config", "Config file not found: {:?}, using defaults", path);
|
||||
return Ok(Self::default());
|
||||
}
|
||||
|
||||
tracing::info!(target: "kernel_config", "Loading config from: {:?}", path);
|
||||
let content = std::fs::read_to_string(&path).map_err(|e| {
|
||||
zclaw_types::ZclawError::Internal(format!("Failed to read config {}: {}", path.display(), e))
|
||||
})?;
|
||||
|
||||
let interpolated = interpolate_env_vars(&content);
|
||||
let mut config: KernelConfig = toml::from_str(&interpolated).map_err(|e| {
|
||||
zclaw_types::ZclawError::Internal(format!("Failed to parse config {}: {}", path.display(), e))
|
||||
})?;
|
||||
|
||||
// Resolve skills_dir if not explicitly set
|
||||
if config.skills_dir.is_none() {
|
||||
config.skills_dir = default_skills_dir();
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
target: "kernel_config",
|
||||
model = %config.llm.model,
|
||||
base_url = %config.llm.base_url,
|
||||
has_api_key = !config.llm.api_key.is_empty(),
|
||||
"Config loaded successfully"
|
||||
);
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
None => Ok(Self::default()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the config file path.
|
||||
fn find_config_path() -> Option<PathBuf> {
|
||||
// 1. Environment variable override
|
||||
if let Ok(path) = std::env::var("ZCLAW_CONFIG") {
|
||||
return Some(PathBuf::from(path));
|
||||
}
|
||||
|
||||
// 2. ~/.zclaw/config.toml
|
||||
if let Some(home) = dirs::home_dir() {
|
||||
let path = home.join(".zclaw").join("config.toml");
|
||||
if path.exists() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Project root config/config.toml (for development)
|
||||
let project_config = std::env::current_dir()
|
||||
.ok()
|
||||
.map(|cwd| cwd.join("config").join("config.toml"))?;
|
||||
|
||||
if project_config.exists() {
|
||||
return Some(project_config);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Create the LLM driver
|
||||
@@ -439,3 +507,81 @@ impl LlmConfig {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// === Environment variable interpolation ===
|
||||
|
||||
/// Replace `${VAR_NAME}` patterns in a string with environment variable values.
|
||||
/// If the variable is not set, the pattern is left as-is.
|
||||
fn interpolate_env_vars(content: &str) -> String {
|
||||
let mut result = String::with_capacity(content.len());
|
||||
let mut chars = content.char_indices().peekable();
|
||||
|
||||
while let Some((_, ch)) = chars.next() {
|
||||
if ch == '$' && chars.peek().map(|(_, c)| *c == '{').unwrap_or(false) {
|
||||
chars.next(); // consume '{'
|
||||
|
||||
let mut var_name = String::new();
|
||||
|
||||
while let Some((_, c)) = chars.peek() {
|
||||
match c {
|
||||
'}' => {
|
||||
chars.next(); // consume '}'
|
||||
if let Ok(value) = std::env::var(&var_name) {
|
||||
result.push_str(&value);
|
||||
} else {
|
||||
result.push_str("${");
|
||||
result.push_str(&var_name);
|
||||
result.push('}');
|
||||
}
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
var_name.push(*c);
|
||||
chars.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle unclosed ${... at end of string
|
||||
if !content[result.len()..].contains('}') && var_name.is_empty() {
|
||||
// Already consumed, nothing to do
|
||||
}
|
||||
} else {
|
||||
result.push(ch);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_interpolate_env_vars_basic() {
|
||||
std::env::set_var("ZCLAW_TEST_VAR", "hello");
|
||||
let result = interpolate_env_vars("prefix ${ZCLAW_TEST_VAR} suffix");
|
||||
assert_eq!(result, "prefix hello suffix");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interpolate_env_vars_missing() {
|
||||
let result = interpolate_env_vars("${ZCLAW_NONEXISTENT_VAR_12345}");
|
||||
assert_eq!(result, "${ZCLAW_NONEXISTENT_VAR_12345}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interpolate_env_vars_no_vars() {
|
||||
let result = interpolate_env_vars("no variables here");
|
||||
assert_eq!(result, "no variables here");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interpolate_env_vars_multiple() {
|
||||
std::env::set_var("ZCLAW_TEST_A", "alpha");
|
||||
std::env::set_var("ZCLAW_TEST_B", "beta");
|
||||
let result = interpolate_env_vars("${ZCLAW_TEST_A}-${ZCLAW_TEST_B}");
|
||||
assert_eq!(result, "alpha-beta");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user