use tracing::info;
use tracing_subscriber::{fmt, EnvFilter};
use tokio::runtime::{Runtime as TokioRuntime, Builder as TokioBuilder};
use xbp_cli::{api, cli, commands};
const MAIN_THREAD_STACK_SIZE: usize = 32 * 1024 * 1024;
const WATCH_THREAD_STACK_SIZE: usize = 2 * 1024 * 1024;
const WATCH_TOKIO_WORKER_THREADS: usize = 2;
fn main() {
let light_watch = wants_lightweight_watch_runtime();
let stack_size = if light_watch {
WATCH_THREAD_STACK_SIZE
} else {
MAIN_THREAD_STACK_SIZE
};
let result = std::thread::Builder::new()
.name(if light_watch {
"xbp-ww-main".into()
} else {
"xbp-main".into()
})
.stack_size(stack_size)
.spawn(move || {
let runtime: TokioRuntime = if light_watch {
TokioBuilder::new_multi_thread()
.worker_threads(WATCH_TOKIO_WORKER_THREADS)
.enable_all()
.thread_name("xbp-ww")
.build()
.expect("failed to build worktree-watch tokio runtime")
} else {
TokioBuilder::new_multi_thread()
.enable_all()
.thread_name("xbp-worker")
.build()
.expect("failed to build tokio runtime")
};
runtime.block_on(async_main())
})
.expect("failed to spawn xbp main thread")
.join();
match result {
Ok(Ok(())) => {}
Ok(Err(code)) => std::process::exit(code),
Err(_) => {
eprintln!("xbp main thread panicked");
std::process::exit(1);
}
}
}
fn wants_lightweight_watch_runtime() -> bool {
if std::env::var_os("XBP_WORKTREE_WATCH_BACKGROUND_CHILD").is_some() {
return true;
}
if std::env::var_os("XBP_WORKTREE_WATCH_TRAY_CHILD").is_some() {
return true;
}
let args: Vec<String> = std::env::args().collect();
let Some(idx) = args.iter().position(|arg| {
arg == "worktree-watch"
|| arg.ends_with("/worktree-watch")
|| arg.ends_with("\\worktree-watch")
}) else {
return false;
};
match args.get(idx + 1).map(String::as_str) {
Some("start") | Some("tray") => true,
_ => false,
}
}
async fn async_main() -> Result<(), i32> {
let background_tray: bool = commands::is_tray_background_child();
if background_tray {
commands::prepare_background_tray_process();
let _ = fmt()
.with_writer(std::io::sink)
.with_ansi(false)
.with_env_filter(EnvFilter::from_default_env())
.try_init();
} else {
fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_ansi(cli::ui::should_emit_ansi())
.init();
}
if std::env::var("PORT_XBP_MCP").is_ok() {
info!("Starting XBP MCP server mode");
if let Err(e) = xbp_mcp::serve_from_env().await {
eprintln!("MCP server error: {}", e);
return Err(1);
}
} else if std::env::var("PORT_XBP_API").is_ok() {
info!("Starting XBP API server mode");
if let Err(e) = api::start_api_server().await {
eprintln!("API server error: {}", e);
return Err(1);
}
} else if let Err(e) = cli::run().await {
if background_tray {
let _ = tracing::error!("{e}");
} else {
eprintln!("{}", e);
}
return Err(1);
}
Ok(())
}