1use std::path::Path;
6
7use crate::engine::EnvSnapshot;
8use crate::error::ToolError;
9use crate::host::Host;
10use crate::liveness::{ProcessProbe, RebindOutcome};
11use crate::logging::{init_file_logging, LogHandle, LogScope};
12use crate::sweep::{startup_sweep, SweepReport};
13
14pub struct StartupReport {
15 pub log: Option<LogHandle>,
16 pub log_warning: Option<String>,
17 pub identity_stderr: Vec<String>,
18 pub sweep: SweepReport,
19 pub rebind: Option<RebindOutcome>,
20}
21
22pub fn render_warning(e: &ToolError) -> String {
24 match &e.hint {
25 Some(h) => format!("{e} ({h})"),
26 None => e.to_string(),
27 }
28}
29
30#[allow(clippy::too_many_arguments)]
31pub fn startup(
32 home: &Path,
33 _host: Host,
34 env: &EnvSnapshot,
35 probe: &dyn ProcessProbe,
36 now_ms: i64,
37 debug: bool,
38 scope: LogScope,
39 identity_warnings: &[ToolError],
40) -> StartupReport {
41 let log_path = home
42 .join("logs")
43 .join(format!("serve-{}.log", std::process::id()));
44 let (log, log_warning) = match init_file_logging(&log_path, debug, scope) {
45 Ok(h) => (Some(h), None),
46 Err(e) => (
47 None,
48 Some(format!(
49 "opencode-ctl: cannot open the log {}: {e}; continuing without a file log",
50 log_path.display()
51 )),
52 ),
53 };
54
55 let mut identity_stderr = Vec::new();
56 for w in identity_warnings {
57 let line = render_warning(w);
58 if log.is_some() {
59 tracing::warn!("{line}");
60 } else {
61 identity_stderr.push(line);
62 }
63 }
64
65 let sweep = startup_sweep(home, now_ms, probe);
66 for e in &sweep.errors {
67 tracing::warn!("startup sweep: {e}");
68 }
69 if !sweep.removed.is_empty() {
70 tracing::info!("startup sweep removed {} entries", sweep.removed.len());
71 }
72
73 let rebind = env.pair.as_deref().and_then(|pair| {
74 let Some(parent) = probe.parent_pid(std::process::id()) else {
75 tracing::warn!("pair {pair}: parent process unknown, no re-bind");
76 return None;
77 };
78 match crate::liveness::rebind_on_spawn(home, pair, parent, probe) {
79 Ok(outcome) => Some(outcome),
80 Err(e) => {
81 tracing::warn!("pair {pair}: re-bind failed: {e}");
82 None
83 }
84 }
85 });
86
87 StartupReport {
88 log,
89 log_warning,
90 identity_stderr,
91 sweep,
92 rebind,
93 }
94}