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