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