Skip to main content

agent_exec/
wait.rs

1//! Implementation of the `wait` sub-command.
2//!
3//! Polls `state.json` until the job leaves the `running` state or a timeout
4//! is reached.
5
6use anyhow::Result;
7use tracing::debug;
8
9use crate::jobstore::{JobDir, resolve_root};
10use crate::schema::{Response, WaitData};
11
12/// Options for the `wait` sub-command.
13#[derive(Debug)]
14pub struct WaitOpts<'a> {
15    pub job_id: &'a str,
16    pub root: Option<&'a str>,
17    /// Poll interval in milliseconds.
18    pub poll_ms: u64,
19    /// Total timeout in milliseconds (default 30000).
20    /// Ignored when `forever` is true.
21    pub until: u64,
22    /// Wait indefinitely when true.
23    pub forever: bool,
24}
25
26impl<'a> Default for WaitOpts<'a> {
27    fn default() -> Self {
28        WaitOpts {
29            job_id: "",
30            root: None,
31            poll_ms: 200,
32            until: 30_000,
33            forever: false,
34        }
35    }
36}
37
38/// Execute `wait`: poll until done, then emit JSON.
39pub fn execute(opts: WaitOpts) -> Result<()> {
40    let root = resolve_root(opts.root);
41    let job_dir = JobDir::open(&root, opts.job_id)?;
42
43    let poll = std::time::Duration::from_millis(opts.poll_ms);
44    let deadline = if opts.forever {
45        None
46    } else {
47        Some(std::time::Instant::now() + std::time::Duration::from_millis(opts.until))
48    };
49
50    loop {
51        let state = job_dir.read_state()?;
52        debug!(job_id = %opts.job_id, state = ?state.status(), "wait poll");
53
54        if !state.status().is_non_terminal() {
55            let response = Response::new(
56                "wait",
57                WaitData {
58                    job_id: job_dir.job_id.clone(),
59                    state: state.status().as_str().to_string(),
60                    exit_code: state.exit_code(),
61                },
62            );
63            response.print();
64            return Ok(());
65        }
66
67        if let Some(dl) = deadline
68            && std::time::Instant::now() >= dl
69        {
70            // Timed out — still in a non-terminal state (created or running).
71            let response = Response::new(
72                "wait",
73                WaitData {
74                    job_id: job_dir.job_id.clone(),
75                    state: state.status().as_str().to_string(),
76                    exit_code: None,
77                },
78            );
79            response.print();
80            return Ok(());
81        }
82
83        std::thread::sleep(poll);
84    }
85}