use anyhow::Result;
use std::sync::Arc;
use tokio::time::{sleep, Duration, Instant};
use crate::board::{render_board, render_task_detail};
use crate::store::Store;
use crate::types::{Task, TaskFilter, TaskStatus};
pub async fn run(
store: &Arc<Store>,
task_ids: &[String],
group: Option<&str>,
exit_on_await: bool,
timeout_secs: Option<u64>,
) -> Result<()> {
let deadline = timeout_secs.map(|secs| Instant::now() + Duration::from_secs(secs));
loop {
print!("\x1b[2J\x1b[H");
if task_ids.len() == 1 {
let id = &task_ids[0];
match store.get_task(id)? {
Some(task) => {
let events = store.get_events(id)?;
let retry_chain = if task.parent_task_id.is_some() {
Some(store.get_retry_chain(id)?)
} else {
None
};
print!("{}", render_task_detail(&task, &events, retry_chain));
if task.status.is_terminal() {
println!("\nTask completed. Exiting watch.");
return Ok(());
}
if exit_on_await && task.status == TaskStatus::AwaitingInput {
print_awaiting_prompt(store, id)?;
return Ok(());
}
}
None => {
println!("Task '{}' not found.", id);
return Ok(());
}
}
} else if task_ids.is_empty() {
let filter = if group.is_some() {
TaskFilter::Active
} else {
TaskFilter::Running
};
let mut running = store.list_tasks(filter)?;
if let Some(group_id) = group {
running.retain(|task| task.workgroup_id.as_deref() == Some(group_id));
}
if running.is_empty() {
println!("No running tasks.");
let mut all = store.list_tasks(TaskFilter::Today)?;
if let Some(group_id) = group {
all.retain(|task| task.workgroup_id.as_deref() == Some(group_id));
}
if !all.is_empty() {
println!();
print!("{}", render_board(&all, store)?);
}
return Ok(());
}
if exit_on_await && exit_on_awaiting_task(store, &running)? {
return Ok(());
}
print!("{}", render_board(&running, store)?);
} else {
let mut tasks = Vec::new();
for id in task_ids {
if let Some(task) = store.get_task(id)? {
tasks.push(task);
}
}
if tasks.is_empty() {
println!("No tasks found.");
return Ok(());
}
print!("{}", render_board(&tasks, store)?);
if exit_on_await && exit_on_awaiting_task(store, &tasks)? {
return Ok(());
}
if tasks.iter().all(|t| t.status.is_terminal()) {
println!("\nAll tasks completed. Exiting watch.");
return Ok(());
}
}
if deadline.is_some_and(|deadline| Instant::now() >= deadline) {
aid_error!("[aid] Timeout after {}s.", timeout_secs.unwrap_or_default());
std::process::exit(124);
}
sleep(Duration::from_secs(1)).await;
}
}
fn exit_on_awaiting_task(store: &Store, tasks: &[Task]) -> Result<bool> {
let Some(task) = tasks.iter().find(|task| task.status == TaskStatus::AwaitingInput) else {
return Ok(false);
};
print_awaiting_prompt(store, task.id.as_str())?;
Ok(true)
}
fn print_awaiting_prompt(store: &Store, task_id: &str) -> Result<()> {
let events = store.get_events(task_id)?;
let prompt = events
.iter()
.rev()
.find_map(|e| {
e.metadata
.as_ref()
.and_then(|m| m.get("awaiting_prompt"))
.and_then(|v| v.as_str())
})
.unwrap_or("");
println!("{task_id} {prompt}");
println!("Use: aid respond {task_id} \"your answer\"");
Ok(())
}