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.
|
/// Load user/bot identity from shared config.
|
||||||
fn load_user_context() -> String {
|
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()
|
let config: serde_json::Value = std::fs::read_to_string(&path).ok()
|
||||||
.and_then(|s| serde_json::from_str(&s).ok())
|
.and_then(|s| serde_json::from_str(&s).ok())
|
||||||
.unwrap_or_default();
|
.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
|
/// Shared config: $cfg/ai.syui.log/config.json
|
||||||
pub fn shared_config_path() -> String {
|
pub fn config_path() -> String {
|
||||||
format!("{}/ai.syui.log/config.json", config_dir())
|
format!("{}/ai.syui.log/config.json", config_dir())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Path to aigpt config (same content as shared, may be symlink)
|
/// Sessions dir: $cfg/ai.syui.log/sessions
|
||||||
pub fn gpt_config_path() -> String {
|
pub fn sessions_dir() -> String {
|
||||||
format!("{}/ai.syui.gpt/config.json", config_dir())
|
format!("{}/ai.syui.log/sessions", config_dir())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Path to sessions dir: $cfg/ai.syui.gpt/sessions
|
/// Load shared config as JSON Value.
|
||||||
pub fn sessions_dir() -> String {
|
pub fn load_config() -> serde_json::Value {
|
||||||
format!("{}/ai.syui.gpt/sessions", config_dir())
|
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)]
|
#[derive(Clone, Deserialize)]
|
||||||
pub struct AgentConfig {
|
pub struct AgentConfig {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -40,6 +67,11 @@ fn default_cwd() -> String {
|
|||||||
.unwrap_or_else(|_| ".".to_string())
|
.unwrap_or_else(|_| ".".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct LoadedConfig {
|
||||||
|
pub agents: Vec<AgentConfig>,
|
||||||
|
pub interval: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Built-in presets.
|
/// Built-in presets.
|
||||||
pub fn preset(name: &str) -> Option<Vec<AgentConfig>> {
|
pub fn preset(name: &str) -> Option<Vec<AgentConfig>> {
|
||||||
let cwd = default_cwd();
|
let cwd = default_cwd();
|
||||||
@@ -70,10 +102,18 @@ pub fn preset(name: &str) -> Option<Vec<AgentConfig>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parsed config with optional loop interval.
|
// ── File loading ───────────────────────────────────────────
|
||||||
pub struct LoadedConfig {
|
|
||||||
pub agents: Vec<AgentConfig>,
|
pub fn load(path: &str) -> Vec<AgentConfig> {
|
||||||
pub interval: Option<u64>,
|
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)]
|
#[derive(Deserialize)]
|
||||||
@@ -85,19 +125,6 @@ struct MultiConfig {
|
|||||||
cwd: Option<String>,
|
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 {
|
fn load_file_full(path: &Path) -> LoadedConfig {
|
||||||
let content = match std::fs::read_to_string(path) {
|
let content = match std::fs::read_to_string(path) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
@@ -118,35 +145,15 @@ fn load_file_full(path: &Path) -> LoadedConfig {
|
|||||||
LoadedConfig { agents, interval: None }
|
LoadedConfig { agents, interval: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Legacy: load agents only from file/directory.
|
|
||||||
fn load_file(path: &Path) -> Vec<AgentConfig> {
|
fn load_file(path: &Path) -> Vec<AgentConfig> {
|
||||||
let content = match std::fs::read_to_string(path) {
|
load_file_full(path).agents
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_dir(path: &Path) -> Vec<AgentConfig> {
|
fn load_dir(path: &Path) -> Vec<AgentConfig> {
|
||||||
let mut entries: Vec<_> = std::fs::read_dir(path)
|
let mut entries: Vec<_> = std::fs::read_dir(path)
|
||||||
.into_iter()
|
.into_iter().flatten().flatten()
|
||||||
.flatten()
|
|
||||||
.flatten()
|
|
||||||
.filter(|e| e.path().extension().is_some_and(|ext| ext == "json"))
|
.filter(|e| e.path().extension().is_some_and(|ext| ext == "json"))
|
||||||
.collect();
|
.collect();
|
||||||
entries.sort_by_key(|e| e.file_name());
|
entries.sort_by_key(|e| e.file_name());
|
||||||
|
|
||||||
entries.iter().flat_map(|e| load_file(&e.path())).collect()
|
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 ────────────────────────────────────
|
// ── Session persistence ────────────────────────────────────
|
||||||
|
|
||||||
fn save_session(cycle: usize, agents: &[Agent], decision: &str, indices: &[usize]) -> String {
|
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 _ = std::fs::create_dir_all(&session_dir);
|
||||||
|
|
||||||
let timestamp = std::time::SystemTime::now()
|
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> {
|
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 content = std::fs::read_to_string(&config_path).ok()?;
|
||||||
let config: serde_json::Value = serde_json::from_str(&content).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)
|
(y, mo + 1, days + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn session_base_dir() -> String {
|
|
||||||
config::sessions_dir()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── AI integration ─────────────────────────────────────────
|
// ── AI integration ─────────────────────────────────────────
|
||||||
|
|
||||||
@@ -881,7 +878,7 @@ pub fn signal_quit() {
|
|||||||
|
|
||||||
/// Update cmd_history.json: deduplicated command log across all agents.
|
/// Update cmd_history.json: deduplicated command log across all agents.
|
||||||
fn update_cmd_history(agents: &[serde_json::Value], session_ts: u64) {
|
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
|
// Load existing
|
||||||
let mut entries: Vec<serde_json::Value> = std::fs::read_to_string(&history_path)
|
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>) {
|
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)
|
let mut files: Vec<_> = std::fs::read_dir(&dir)
|
||||||
.into_iter().flatten().flatten()
|
.into_iter().flatten().flatten()
|
||||||
.filter(|e| {
|
.filter(|e| {
|
||||||
@@ -1037,7 +1034,7 @@ pub fn context() {
|
|||||||
|
|
||||||
// Session info
|
// Session info
|
||||||
let dec_path = format!("{STATE_DIR}/decision.json");
|
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)
|
let session_count = std::fs::read_dir(&session_dir)
|
||||||
.into_iter().flatten().flatten()
|
.into_iter().flatten().flatten()
|
||||||
.filter(|e| e.path().extension().is_some_and(|x| x == "json") && e.file_name().to_string_lossy() != "cmd_history.json")
|
.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)
|
// 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(content) = std::fs::read_to_string(&history_path) {
|
||||||
if let Ok(entries) = serde_json::from_str::<Vec<serde_json::Value>>(&content) {
|
if let Ok(entries) = serde_json::from_str::<Vec<serde_json::Value>>(&content) {
|
||||||
if !entries.is_empty() {
|
if !entries.is_empty() {
|
||||||
@@ -1155,7 +1152,7 @@ pub fn context() {
|
|||||||
|
|
||||||
/// Clean old sessions, keep last N.
|
/// Clean old sessions, keep last N.
|
||||||
pub fn history_clean() {
|
pub fn history_clean() {
|
||||||
let dir = session_base_dir();
|
let dir = config::sessions_dir();
|
||||||
let mut files: Vec<_> = std::fs::read_dir(&dir)
|
let mut files: Vec<_> = std::fs::read_dir(&dir)
|
||||||
.into_iter().flatten().flatten()
|
.into_iter().flatten().flatten()
|
||||||
.filter(|e| {
|
.filter(|e| {
|
||||||
@@ -1182,7 +1179,7 @@ pub fn history_cmd(filter: Option<&str>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn show_cmd_history(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) {
|
match std::fs::read_to_string(&path) {
|
||||||
Ok(content) => {
|
Ok(content) => {
|
||||||
if let Ok(entries) = serde_json::from_str::<Vec<serde_json::Value>>(&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 {
|
fn expand_tilde(path: &str) -> String {
|
||||||
if let Some(rest) = path.strip_prefix('~') {
|
config::expand_tilde(path)
|
||||||
if let Ok(home) = std::env::var("HOME") {
|
|
||||||
return format!("{home}{rest}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
path.to_string()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -664,7 +664,7 @@ fn parse_agent_commands(text: &str) -> Vec<(String, String, String)> {
|
|||||||
|
|
||||||
/// Load identity context from atproto config + recent chat.
|
/// Load identity context from atproto config + recent chat.
|
||||||
fn load_identity_context() -> String {
|
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) {
|
let config: serde_json::Value = match std::fs::read_to_string(&config_path) {
|
||||||
Ok(s) => serde_json::from_str(&s).unwrap_or_default(),
|
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.
|
/// Watch a directory for file changes.
|
||||||
/// On change, write next=true to loop.json with changed file list.
|
/// On change, write next=true to loop.json with changed file list.
|
||||||
pub fn run(dir: &str, config: Option<&str>) -> Result<(), String> {
|
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);
|
let dir_path = Path::new(&dir);
|
||||||
if !dir_path.is_dir() {
|
if !dir_path.is_dir() {
|
||||||
return Err(format!("not a directory: {dir}"));
|
return Err(format!("not a directory: {dir}"));
|
||||||
|
|||||||
Reference in New Issue
Block a user