wsb 0.291.8591

Workspace tools for every project under $PRJ: git across submodules, template stamping, GitHub, and the workbench
Documentation
//! The daemon, as the command line sees it.
//!
//! wsb is two programs. `wsb` is a command line that starts, does one thing
//! and exits; `wsb-daemon` is a process that stays and serves the workbench.
//! This module is everything the first needs to find, start and talk to the
//! second — the REST contract they share, the files they meet at, and the
//! rules about which daemon is the right one to talk to.
//!
//! # Why a daemon at all
//!
//! A command line cannot serve the workbench, which has to outlive whatever
//! started it or somebody has to keep a terminal open for it. The daemon also
//! keeps the workshop's GitHub mirror current and carries peer continuity.
//!
//! # What the daemon is not
//!
//! It is not a place work happens. Commands still do their own work in their
//! own process; the daemon hosts the workbench. `--no-daemon` therefore loses
//! the workbench and changes nothing about what a command does.
//!
//! # One daemon, one WORKSHOP
//!
//! The daemon serves every worktree on this host, from one process at one
//! port, with its state in the host folder (see [`workshop`]). It was one
//! daemon per checkout, and that made a workshop with six checkouts run six
//! daemons, bind six ports and hold six copies of everything — and left peer
//! identity, which is a property of the WORKSHOP, with nowhere to live.

pub mod client;
pub mod workshop;
pub mod launch;
pub mod protocol;
pub mod rendezvous;
pub mod service;
pub mod lifecycle;

pub use client::Client;
pub use launch::{
    cli_executable, daemon_arguments, daemon_executable, ensure_running, log_path,
    wait_until_stopped, Port, Spawn,
};
pub use protocol::{route, ApiError, Info, WorktreeInfo};
pub use workshop::{
    adopt_old_state_dir, default_workshop_name, family_of, home_dir, identify_shared, host_dir, workshop_name,
    new_control_token, HOST_DIR_ENV, WORKSHOP_NAME_ENV,
    place_worktree, staging_dir, Continuity, WorkshopRendezvous, RegisteredWorktree, WorktreeId,
    WorktreePlace,
};
pub use rendezvous::Rendezvous;

/// Saying "no daemon" to every wsb in a run, not just the first.
///
/// `--no-daemon` sets it, and every wsb started underneath reads it. Without
/// that, a command told to stay in one process would have children starting
/// daemons behind it — and the operator would find one running after a run
/// they explicitly asked to keep local.
///
/// It is also how a test harness declares that it must not touch this
/// workshop's daemon: the suites drive wsb against fixture worktrees, and a
/// daemon started there would outlive the temporary directory it was serving.
pub const NO_DAEMON_ENV: &str = "WSB_NO_DAEMON";

/// Whether this run has been told to keep to itself.
///
/// One reading of the variable, so "does this run use the daemon" is one
/// question with one answer rather than a condition each caller writes out.
pub fn disabled() -> bool {
    std::env::var_os(NO_DAEMON_ENV).is_some_and(|value| !value.is_empty())
}

/// The status a daemon exits with when another is already serving the
/// workshop. A service is defined not to start one again on it: restarted, it
/// would find the same daemon and refuse again, for as long as that one runs.
pub const ANOTHER_SERVES_EXIT: i32 = 78;

/// Set in a service's environment to the supervisor keeping it (`systemd`,
/// `launchd`). launchd cannot be told which failures not to restart, only
/// that a clean exit is not one, so a daemon it keeps leaves cleanly when it
/// refuses to start beside another.
pub const SUPERVISOR_ENV: &str = "WSB_DAEMON_SUPERVISOR";

/// Where a daemon listens unless somebody says otherwise.
///
/// This host and nothing else. The workbench has no authentication — it
/// reads and edits the worktree and runs git on it — so who can reach
/// it is a decision an operator makes deliberately, per start, with
/// `wsb serve --listen`. It is
/// not a stored preference: a daemon that came back up reachable because of
/// something switched on months ago is the failure this default exists to
/// prevent.
pub const LOOPBACK: std::net::IpAddr = std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST);

/// The default port the workbench is served on.
///
/// Where an unnamed port STARTS. A daemon told to take an exact port and
/// finding it held says so and stops; one that was not told takes the first
/// free port from here and publishes it, because the alternative is that the
/// second worktree on a host can never have a daemon at all.
pub const DEFAULT_HTTP_PORT: u16 = 3000;