use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use crate::paths::routines_dir;
use crate::routines::{Routine, RoutineStore};
use crate::utils::lock::LockRecover;
use super::{read_routine_cron, read_routine_toml, read_runtime_state};
#[path = "routine_storage_walk.rs"]
mod routine_storage_walk;
fn read_last_log_timestamp(path: &std::path::Path) -> Option<u64> {
let text = std::fs::read_to_string(path).ok()?;
text.lines()
.rev()
.find_map(|line| line.trim().parse::<u64>().ok())
}
fn read_scheduled_state(base: &std::path::Path, dir_name: &str) -> Option<u64> {
read_last_log_timestamp(&base.join(dir_name).join("scheduled.log"))
}
fn read_manual_state(base: &std::path::Path, dir_name: &str) -> Option<u64> {
read_last_log_timestamp(&base.join(dir_name).join("manual.log"))
}
fn read_pure_prompt(base: &std::path::Path, dir_name: &str, legacy: Option<String>) -> String {
std::fs::read_to_string(base.join(dir_name).join("prompts").join("prompt.pure.md"))
.ok()
.or(legacy)
.unwrap_or_default()
}
pub(super) fn load_routine_from_dir(dir_name: &str) -> Option<Routine> {
load_routine_from_base(&routines_dir(), dir_name)
}
fn load_routine_from_base(base: &std::path::Path, dir_name: &str) -> Option<Routine> {
let toml = read_routine_toml(&base.join(dir_name).join("routine.toml"))?;
let title = toml.title?;
let id = toml.id.unwrap_or_else(|| dir_name.to_string());
let runtime_state = read_runtime_state(base, dir_name);
let schedule = toml
.schedule
.or_else(|| read_routine_cron(&base.join(dir_name).join("schedule.cron")))?;
let last_manual_trigger_at = read_manual_state(base, dir_name)
.or(runtime_state.last_manual_trigger_at)
.or(toml.last_manual_trigger_at);
let last_scheduled_trigger_at = read_scheduled_state(base, dir_name);
let prompt = read_pure_prompt(base, dir_name, toml.prompt);
Some(Routine {
id,
schedule,
title,
agent: toml.agent?,
model: toml.model,
prompt,
goal: toml.goal,
repositories: toml.repositories,
machines: toml.machines,
enabled: toml.enabled.unwrap_or(true),
source: "managed".to_string(),
created_at: toml.created_at.unwrap_or(0),
updated_at: toml.updated_at.unwrap_or(0),
last_manual_trigger_at,
last_scheduled_trigger_at,
snoozed_until: runtime_state.snoozed_until,
skip_runs: runtime_state.skip_runs,
power_saving: runtime_state.power_saving,
ttl_secs: toml.ttl_secs,
max_runtime_secs: toml.max_runtime_secs,
tags: toml.tags,
env: toml.env,
})
}
pub fn load_store() -> RoutineStore {
load_store_from_dir(&routines_dir())
}
pub(crate) fn load_store_from_dir(dir: &std::path::Path) -> RoutineStore {
Arc::new(Mutex::new(scan_routines(dir)))
}
pub(crate) fn reload_store_from_dir(store: &RoutineStore, dir: &std::path::Path) {
let fresh = scan_routines(dir);
*store.lock_recover() = fresh;
}
fn scan_routines(dir: &std::path::Path) -> HashMap<String, Routine> {
let mut routines = HashMap::new();
routine_storage_walk::walk_routines(dir, dir, &mut routines, &load_routine_from_base);
routines
}