refactor(config): unify all config to ai.syui.log, remove ai.syui.gpt config dependency
This commit is contained in:
@@ -292,7 +292,7 @@ impl Drop for Agent {
|
||||
|
||||
/// Load user/bot identity from shared config.
|
||||
fn load_user_context() -> String {
|
||||
let path = crate::config::shared_config_path();
|
||||
let path = crate::config::config_path();
|
||||
let config: serde_json::Value = std::fs::read_to_string(&path).ok()
|
||||
.and_then(|s| serde_json::from_str(&s).ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
101
src/config.rs
101
src/config.rs
@@ -11,21 +11,48 @@ pub fn config_dir() -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Path to shared config: $cfg/ai.syui.log/config.json
|
||||
pub fn shared_config_path() -> String {
|
||||
/// Shared config: $cfg/ai.syui.log/config.json
|
||||
pub fn config_path() -> String {
|
||||
format!("{}/ai.syui.log/config.json", config_dir())
|
||||
}
|
||||
|
||||
/// Path to aigpt config (same content as shared, may be symlink)
|
||||
pub fn gpt_config_path() -> String {
|
||||
format!("{}/ai.syui.gpt/config.json", config_dir())
|
||||
/// Sessions dir: $cfg/ai.syui.log/sessions
|
||||
pub fn sessions_dir() -> String {
|
||||
format!("{}/ai.syui.log/sessions", config_dir())
|
||||
}
|
||||
|
||||
/// Path to sessions dir: $cfg/ai.syui.gpt/sessions
|
||||
pub fn sessions_dir() -> String {
|
||||
format!("{}/ai.syui.gpt/sessions", config_dir())
|
||||
/// Load shared config as JSON Value.
|
||||
pub fn load_config() -> serde_json::Value {
|
||||
std::fs::read_to_string(config_path()).ok()
|
||||
.and_then(|s| serde_json::from_str(&s).ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Resolve bot data dir from config's bot.path field.
|
||||
/// Falls back to $cfg/ai.syui.log/at
|
||||
pub fn bot_data_dir() -> String {
|
||||
let config = load_config();
|
||||
let path = config["bot"]["path"].as_str().unwrap_or("");
|
||||
let did = config["bot"]["did"].as_str().unwrap_or("did");
|
||||
let expanded = expand_tilde(path);
|
||||
if expanded.is_empty() {
|
||||
format!("{}/ai.syui.log/at/{did}", config_dir())
|
||||
} else {
|
||||
format!("{expanded}/{did}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_tilde(path: &str) -> String {
|
||||
if let Some(rest) = path.strip_prefix('~') {
|
||||
if let Ok(home) = std::env::var("HOME") {
|
||||
return format!("{home}{rest}");
|
||||
}
|
||||
}
|
||||
path.to_string()
|
||||
}
|
||||
|
||||
// ── Agent configs ──────────────────────────────────────────
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct AgentConfig {
|
||||
pub name: String,
|
||||
@@ -40,6 +67,11 @@ fn default_cwd() -> String {
|
||||
.unwrap_or_else(|_| ".".to_string())
|
||||
}
|
||||
|
||||
pub struct LoadedConfig {
|
||||
pub agents: Vec<AgentConfig>,
|
||||
pub interval: Option<u64>,
|
||||
}
|
||||
|
||||
/// Built-in presets.
|
||||
pub fn preset(name: &str) -> Option<Vec<AgentConfig>> {
|
||||
let cwd = default_cwd();
|
||||
@@ -70,10 +102,18 @@ pub fn preset(name: &str) -> Option<Vec<AgentConfig>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsed config with optional loop interval.
|
||||
pub struct LoadedConfig {
|
||||
pub agents: Vec<AgentConfig>,
|
||||
pub interval: Option<u64>,
|
||||
// ── File loading ───────────────────────────────────────────
|
||||
|
||||
pub fn load(path: &str) -> Vec<AgentConfig> {
|
||||
load_full(path).agents
|
||||
}
|
||||
|
||||
pub fn load_full(path: &str) -> LoadedConfig {
|
||||
let path = Path::new(path);
|
||||
if path.is_dir() {
|
||||
return LoadedConfig { agents: load_dir(path), interval: None };
|
||||
}
|
||||
load_file_full(path)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -85,19 +125,6 @@ struct MultiConfig {
|
||||
cwd: Option<String>,
|
||||
}
|
||||
|
||||
pub fn load(path: &str) -> Vec<AgentConfig> {
|
||||
load_full(path).agents
|
||||
}
|
||||
|
||||
/// Load config with metadata (interval etc).
|
||||
pub fn load_full(path: &str) -> LoadedConfig {
|
||||
let path = Path::new(path);
|
||||
if path.is_dir() {
|
||||
return LoadedConfig { agents: load_dir(path), interval: None };
|
||||
}
|
||||
load_file_full(path)
|
||||
}
|
||||
|
||||
fn load_file_full(path: &Path) -> LoadedConfig {
|
||||
let content = match std::fs::read_to_string(path) {
|
||||
Ok(c) => c,
|
||||
@@ -118,35 +145,15 @@ fn load_file_full(path: &Path) -> LoadedConfig {
|
||||
LoadedConfig { agents, interval: None }
|
||||
}
|
||||
|
||||
/// Legacy: load agents only from file/directory.
|
||||
fn load_file(path: &Path) -> Vec<AgentConfig> {
|
||||
let content = match std::fs::read_to_string(path) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return Vec::new(),
|
||||
};
|
||||
|
||||
// Try as { "agents": [...] } or single { "name", "task", "cwd" }
|
||||
if let Ok(multi) = serde_json::from_str::<MultiConfig>(&content) {
|
||||
if let Some(agents) = multi.agents {
|
||||
return agents;
|
||||
}
|
||||
if let (Some(name), Some(task), Some(cwd)) = (multi.name, multi.task, multi.cwd) {
|
||||
return vec![AgentConfig { name, task, cwd }];
|
||||
}
|
||||
}
|
||||
|
||||
// Try as bare array [{ ... }, { ... }]
|
||||
serde_json::from_str::<Vec<AgentConfig>>(&content).unwrap_or_default()
|
||||
load_file_full(path).agents
|
||||
}
|
||||
|
||||
fn load_dir(path: &Path) -> Vec<AgentConfig> {
|
||||
let mut entries: Vec<_> = std::fs::read_dir(path)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flatten()
|
||||
.into_iter().flatten().flatten()
|
||||
.filter(|e| e.path().extension().is_some_and(|ext| ext == "json"))
|
||||
.collect();
|
||||
entries.sort_by_key(|e| e.file_name());
|
||||
|
||||
entries.iter().flat_map(|e| load_file(&e.path())).collect()
|
||||
}
|
||||
|
||||
@@ -499,7 +499,7 @@ fn wait_for_loop_signal(running: &Arc<AtomicBool>) -> LoopAction {
|
||||
// ── Session persistence ────────────────────────────────────
|
||||
|
||||
fn save_session(cycle: usize, agents: &[Agent], decision: &str, indices: &[usize]) -> String {
|
||||
let session_dir = session_base_dir();
|
||||
let session_dir = config::sessions_dir();
|
||||
let _ = std::fs::create_dir_all(&session_dir);
|
||||
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
@@ -575,7 +575,7 @@ fn save_to_aigpt_memory(decision: &str, agents: &[serde_json::Value]) {
|
||||
}
|
||||
|
||||
fn aigpt_memory_dir() -> Option<String> {
|
||||
let config_path = config::shared_config_path();
|
||||
let config_path = config::config_path();
|
||||
let content = std::fs::read_to_string(&config_path).ok()?;
|
||||
let config: serde_json::Value = serde_json::from_str(&content).ok()?;
|
||||
|
||||
@@ -621,9 +621,6 @@ fn days_to_ymd(mut days: u64) -> (u64, u64, u64) {
|
||||
(y, mo + 1, days + 1)
|
||||
}
|
||||
|
||||
fn session_base_dir() -> String {
|
||||
config::sessions_dir()
|
||||
}
|
||||
|
||||
// ── AI integration ─────────────────────────────────────────
|
||||
|
||||
@@ -881,7 +878,7 @@ pub fn signal_quit() {
|
||||
|
||||
/// Update cmd_history.json: deduplicated command log across all agents.
|
||||
fn update_cmd_history(agents: &[serde_json::Value], session_ts: u64) {
|
||||
let history_path = format!("{}/cmd_history.json", session_base_dir());
|
||||
let history_path = format!("{}/cmd_history.json", config::sessions_dir());
|
||||
|
||||
// Load existing
|
||||
let mut entries: Vec<serde_json::Value> = std::fs::read_to_string(&history_path)
|
||||
@@ -953,7 +950,7 @@ fn update_cmd_history(agents: &[serde_json::Value], session_ts: u64) {
|
||||
}
|
||||
|
||||
pub fn history(detail: Option<&str>) {
|
||||
let dir = session_base_dir();
|
||||
let dir = config::sessions_dir();
|
||||
let mut files: Vec<_> = std::fs::read_dir(&dir)
|
||||
.into_iter().flatten().flatten()
|
||||
.filter(|e| {
|
||||
@@ -1037,7 +1034,7 @@ pub fn context() {
|
||||
|
||||
// Session info
|
||||
let dec_path = format!("{STATE_DIR}/decision.json");
|
||||
let session_dir = session_base_dir();
|
||||
let session_dir = config::sessions_dir();
|
||||
let session_count = std::fs::read_dir(&session_dir)
|
||||
.into_iter().flatten().flatten()
|
||||
.filter(|e| e.path().extension().is_some_and(|x| x == "json") && e.file_name().to_string_lossy() != "cmd_history.json")
|
||||
@@ -1137,7 +1134,7 @@ pub fn context() {
|
||||
}
|
||||
|
||||
// Command history (last 10)
|
||||
let history_path = format!("{}/cmd_history.json", session_base_dir());
|
||||
let history_path = format!("{}/cmd_history.json", config::sessions_dir());
|
||||
if let Ok(content) = std::fs::read_to_string(&history_path) {
|
||||
if let Ok(entries) = serde_json::from_str::<Vec<serde_json::Value>>(&content) {
|
||||
if !entries.is_empty() {
|
||||
@@ -1155,7 +1152,7 @@ pub fn context() {
|
||||
|
||||
/// Clean old sessions, keep last N.
|
||||
pub fn history_clean() {
|
||||
let dir = session_base_dir();
|
||||
let dir = config::sessions_dir();
|
||||
let mut files: Vec<_> = std::fs::read_dir(&dir)
|
||||
.into_iter().flatten().flatten()
|
||||
.filter(|e| {
|
||||
@@ -1182,7 +1179,7 @@ pub fn history_cmd(filter: Option<&str>) {
|
||||
}
|
||||
|
||||
fn show_cmd_history(filter: Option<&str>) {
|
||||
let path = format!("{}/cmd_history.json", session_base_dir());
|
||||
let path = format!("{}/cmd_history.json", config::sessions_dir());
|
||||
match std::fs::read_to_string(&path) {
|
||||
Ok(content) => {
|
||||
if let Ok(entries) = serde_json::from_str::<Vec<serde_json::Value>>(&content) {
|
||||
@@ -1279,13 +1276,6 @@ fn create_state_dir() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_tilde_pub(path: &str) -> String { expand_tilde(path) }
|
||||
|
||||
fn expand_tilde(path: &str) -> String {
|
||||
if let Some(rest) = path.strip_prefix('~') {
|
||||
if let Ok(home) = std::env::var("HOME") {
|
||||
return format!("{home}{rest}");
|
||||
}
|
||||
}
|
||||
path.to_string()
|
||||
config::expand_tilde(path)
|
||||
}
|
||||
|
||||
@@ -664,7 +664,7 @@ fn parse_agent_commands(text: &str) -> Vec<(String, String, String)> {
|
||||
|
||||
/// Load identity context from atproto config + recent chat.
|
||||
fn load_identity_context() -> String {
|
||||
let config_path = crate::config::shared_config_path();
|
||||
let config_path = crate::config::config_path();
|
||||
|
||||
let config: serde_json::Value = match std::fs::read_to_string(&config_path) {
|
||||
Ok(s) => serde_json::from_str(&s).unwrap_or_default(),
|
||||
|
||||
@@ -9,7 +9,7 @@ const DEBOUNCE_MS: u64 = 2000;
|
||||
/// Watch a directory for file changes.
|
||||
/// On change, write next=true to loop.json with changed file list.
|
||||
pub fn run(dir: &str, config: Option<&str>) -> Result<(), String> {
|
||||
let dir = crate::headless::expand_tilde_pub(dir);
|
||||
let dir = crate::config::expand_tilde(dir);
|
||||
let dir_path = Path::new(&dir);
|
||||
if !dir_path.is_dir() {
|
||||
return Err(format!("not a directory: {dir}"));
|
||||
|
||||
Reference in New Issue
Block a user