use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::error::CliResult;
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
pub struct CliConfig {
pub base_url: Option<String>,
pub workspace_id: Option<Uuid>,
pub data_dir: Option<PathBuf>,
pub log_level: Option<String>,
}
impl CliConfig {
pub fn config_dir() -> PathBuf {
if let Ok(d) = std::env::var("CLAW_DATA_DIR") {
PathBuf::from(d)
} else {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".clawdb")
}
}
pub fn load() -> CliResult<Self> {
let path = Self::config_dir().join("config.toml");
if !path.exists() {
return Ok(Self::default());
}
let content = std::fs::read_to_string(&path)?;
let cfg: Self = toml::from_str(&content)?;
Ok(cfg)
}
pub fn save(&self) -> CliResult<()> {
let dir = Self::config_dir();
std::fs::create_dir_all(&dir)?;
let path = dir.join("config.toml");
let content = toml::to_string_pretty(self)?;
std::fs::write(path, content)?;
Ok(())
}
}
pub fn load_session_token() -> Option<String> {
let path = CliConfig::config_dir().join("session.token");
if !path.exists() {
return None;
}
check_token_permissions(&path);
std::fs::read_to_string(&path)
.ok()
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
}
pub fn save_session_token(token: &str) -> CliResult<()> {
let dir = CliConfig::config_dir();
std::fs::create_dir_all(&dir)?;
let path = dir.join("session.token");
std::fs::write(&path, token)?;
set_token_permissions(&path)?;
Ok(())
}
#[cfg(unix)]
fn check_token_permissions(path: &std::path::Path) {
use std::os::unix::fs::MetadataExt;
if let Ok(meta) = std::fs::metadata(path) {
if meta.mode() & 0o777 != 0o600 {
eprintln!(
"⚠ Warning: {} has insecure permissions — run: chmod 600 {}",
path.display(),
path.display()
);
}
}
}
#[cfg(not(unix))]
fn check_token_permissions(_path: &std::path::Path) {}
#[cfg(unix)]
fn set_token_permissions(path: &std::path::Path) -> CliResult<()> {
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600))?;
Ok(())
}
#[cfg(not(unix))]
fn set_token_permissions(_path: &std::path::Path) -> CliResult<()> {
Ok(())
}
pub fn resolve_base_url(from_cli: &str, cfg: &CliConfig) -> String {
let default = "http://localhost:8080";
if from_cli != default {
return from_cli.to_string();
}
cfg.base_url.clone().unwrap_or_else(|| default.to_string())
}