xbp 10.57.0

XBP is a zero-config build pack that can also interact with proxies, kafka, sockets, synthetic monitors.
Documentation
use tracing::info;
use tracing_subscriber::{fmt, EnvFilter};
use tokio::runtime::{Runtime as TokioRuntime, Builder as TokioBuilder};

use xbp_cli::{api, cli, commands};

/// Windows defaults to a ~1MB main-thread stack. Long `version release` /
/// `publish` async graphs (plus stream capture) have overflowed that after
/// multi-minute `cargo publish` runs. Run the runtime on a larger stack.
const MAIN_THREAD_STACK_SIZE: usize = 32 * 1024 * 1024;
/// Long-lived worktree-watch / tray do not need a 32 MiB stack or a
/// num_cpus-sized Tokio pool (that alone was a large chunk of the ~1 GiB RSS).
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);
        }
    }
}

/// `worktree-watch start` / `tray` (and their detached children) are long-lived
/// and mostly sync FS loops — do not pay full CLI runtime costs.
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(())
}