Skip to main content

agent_procs/cli/
up.rs

1use crate::cli;
2use crate::config::{load_config, resolve_session};
3use crate::protocol::{Request, Response};
4use futures::future::join_all;
5
6pub async fn execute(
7    cli_session: Option<&str>,
8    only: Option<&str>,
9    config_path: Option<&str>,
10) -> i32 {
11    let (path, config) = match load_config(config_path) {
12        Ok(c) => c,
13        Err(e) => {
14            eprintln!("error: {}", e);
15            return 1;
16        }
17    };
18
19    let session = resolve_session(cli_session, config.session.as_deref());
20
21    let only_set: Option<Vec<&str>> = only.map(|s| s.split(',').collect());
22
23    let groups = match config.startup_order() {
24        Ok(g) => g,
25        Err(e) => {
26            eprintln!("error: {}", e);
27            return 1;
28        }
29    };
30
31    for group in &groups {
32        let names: Vec<&String> = group
33            .iter()
34            .filter(|name| {
35                only_set
36                    .as_ref()
37                    .is_none_or(|only| only.contains(&name.as_str()))
38            })
39            .collect();
40
41        // Start all processes in this group concurrently
42        let start_futures: Vec<_> = names
43            .iter()
44            .map(|name| {
45                let def = &config.processes[*name];
46
47                let resolved_cwd = def.cwd.as_ref().map(|c| {
48                    let p = std::path::Path::new(c);
49                    if p.is_relative() {
50                        path.parent()
51                            .unwrap_or(std::path::Path::new("."))
52                            .join(p)
53                            .to_string_lossy()
54                            .to_string()
55                    } else {
56                        c.clone()
57                    }
58                });
59
60                let env = if def.env.is_empty() {
61                    None
62                } else {
63                    Some(def.env.clone())
64                };
65
66                let req = Request::Run {
67                    command: def.cmd.clone(),
68                    name: Some((*name).clone()),
69                    cwd: resolved_cwd,
70                    env,
71                };
72                let name = (*name).clone();
73                async move {
74                    let result = cli::request(session, &req, true).await;
75                    (name, result)
76                }
77            })
78            .collect();
79
80        let results = join_all(start_futures).await;
81
82        for (name, result) in &results {
83            match result {
84                Ok(Response::RunOk { name, id, pid, .. }) => {
85                    println!("started {} (id: {}, pid: {})", name, id, pid);
86                }
87                Ok(Response::Error { code, message }) => {
88                    eprintln!("error starting {}: {}", name, message);
89                    return *code;
90                }
91                _ => return 1,
92            }
93        }
94
95        // Wait for ready patterns concurrently within the group
96        let ready_futures: Vec<_> = names
97            .iter()
98            .filter_map(|name| {
99                let def = &config.processes[*name];
100                def.ready.as_ref().map(|ready| {
101                    let req = Request::Wait {
102                        target: (*name).clone(),
103                        until: Some(ready.clone()),
104                        regex: false,
105                        exit: false,
106                        timeout_secs: Some(30),
107                    };
108                    let name = (*name).clone();
109                    async move {
110                        let result = cli::request(session, &req, false).await;
111                        (name, result)
112                    }
113                })
114            })
115            .collect();
116
117        let ready_results = join_all(ready_futures).await;
118
119        for (name, result) in &ready_results {
120            match result {
121                Ok(Response::WaitMatch { .. }) => println!("{} is ready", name),
122                Ok(Response::WaitTimeout) => {
123                    eprintln!("warning: {} did not become ready within 30s", name);
124                }
125                Ok(Response::Error { message, .. }) => {
126                    eprintln!("error waiting for {}: {}", name, message);
127                    return 1;
128                }
129                _ => {}
130            }
131        }
132    }
133
134    println!("all processes started");
135    0
136}