#![allow(
clippy::wildcard_imports,
clippy::too_many_arguments,
clippy::needless_pass_by_value,
dead_code,
reason = "split-out module preserves existing code while keeping files under the linecheck limit"
)]
use super::*;
pub(crate) fn read_runtime_state(base: &std::path::Path, dir_name: &str) -> RuntimeState {
std::fs::read_to_string(base.join(dir_name).join("state.local.toml"))
.ok()
.and_then(|text| toml::from_str(&text).ok())
.unwrap_or_default()
}
pub(crate) fn read_local_env(id: &str) -> HashMap<String, String> {
std::fs::read_to_string(crate::paths::routine_local_toml_path(id))
.ok()
.and_then(|text| toml::from_str::<RoutineLocalToml>(&text).ok())
.map(|local| local.env)
.unwrap_or_default()
}
pub(crate) fn local_env_keys(id: &str) -> Vec<String> {
read_local_env(id).into_keys().collect()
}
pub fn write_routine(routine: &Routine) -> std::io::Result<()> {
let rel_dir = routine_storage_location::routine_rel_dir(routine);
write_routine_to_rel_dir(routine, &rel_dir)
}
pub(crate) fn write_routine_to_rel_dir(routine: &Routine, rel_dir: &str) -> std::io::Result<()> {
let dir = routine_dir(rel_dir);
if let Some(existing_id) =
read_routine_toml(&routine_toml_path(rel_dir)).and_then(|existing| existing.id)
{
if existing_id != routine.id {
return Err(std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
format!(
"routine dir \"{rel_dir}\" is already used on disk by routine {existing_id}; refusing to overwrite it"
),
));
}
}
crate::utils::fs_perms::create_private_dir_all(&dir)?;
crate::utils::fs_perms::create_private_dir_all(&routine_prompts_dir(rel_dir))?;
let _ = std::fs::remove_file(routine_script_path(rel_dir));
let toml_routine = RoutineToml {
id: Some(routine.id.clone()),
title: Some(routine.title.clone()),
agent: Some(routine.agent.clone()),
model: routine.model.clone(),
prompt: None,
goal: routine.goal.clone(),
repositories: routine.repositories.clone(),
machines: routine.machines.clone(),
enabled: Some(routine.enabled),
power_saving_exempt: routine.power_saving_exempt,
created_at: Some(routine.created_at),
updated_at: Some(routine.updated_at),
last_manual_trigger_at: None,
ttl_secs: routine.ttl_secs,
max_runtime_secs: routine.max_runtime_secs,
failure_threshold: routine.failure_threshold,
tags: routine.tags.clone(),
env: routine.env.clone(),
};
let text = toml::to_string_pretty(&toml_routine).map_err(std::io::Error::other)?;
atomic_write(&routine_toml_path(rel_dir), text.as_bytes())?;
atomic_write(
&routine_cron_path(rel_dir),
format!("{}\n", routine.effective_schedules().join("\n")).as_bytes(),
)?;
atomic_write(
&routine_pure_prompt_path(rel_dir),
routine.prompt.as_bytes(),
)?;
atomic_write(
&routine_compiled_prompt_path(rel_dir),
compose_prompt(routine).as_bytes(),
)?;
write_runtime_state(rel_dir, routine)?;
Ok(())
}
pub(crate) fn write_runtime_state(slug: &str, routine: &Routine) -> std::io::Result<()> {
let path = routine_state_path(slug);
if routine.snoozed_until.is_none()
&& routine.skip_runs.is_none()
&& !routine.power_saving
&& routine.consecutive_failures == 0
&& routine.auto_disabled_reason.is_none()
{
if path.exists() {
std::fs::remove_file(&path)?;
}
return Ok(());
}
let state = RuntimeState {
last_manual_trigger_at: None,
snoozed_until: routine.snoozed_until,
skip_runs: routine.skip_runs,
power_saving: routine.power_saving,
consecutive_failures: routine.consecutive_failures,
auto_disabled_reason: routine.auto_disabled_reason.clone(),
};
let text = toml::to_string_pretty(&state).map_err(std::io::Error::other)?;
atomic_write(&path, text.as_bytes())?;
Ok(())
}
include!("append_manual_trigger_log.rs");