brgr-cli 2.15.10

Let Codex or Claude Code hand work to other coding agents, each in its own Herdr pane: the brgr CLI and Herdr plugin.
//! Host-side checks for commands that arrive through the Herdr plugin bridge.

use std::{
    env,
    path::{Path, PathBuf},
};

use anyhow::{Context as _, Result, bail};

use crate::{
    cli::{ArtifactCommand, Cli, Command, ConfigCommand, ErrorsCommand, HarnessCommand},
    message, plugin_bridge,
};

pub(crate) fn bridge_budget_seconds(command: &Command) -> u64 {
    match command {
        Command::Run(args) if args.foreground => args
            .deadline_seconds
            .unwrap_or(plugin_bridge::MAX_BRIDGE_SECONDS - 120),
        Command::Revise(args) if args.foreground => {
            plugin_bridge::MAX_BRIDGE_SECONDS.saturating_sub(120)
        }
        Command::Wait {
            timeout_seconds, ..
        }
        | Command::Watch {
            timeout_seconds, ..
        }
        | Command::Message {
            command:
                message::MessageCommand::Wait {
                    timeout_seconds, ..
                },
        } => *timeout_seconds,
        _ => 3_600,
    }
}

pub(crate) fn validate_bridge_host_preflight(cli: &Cli) -> Result<()> {
    let host_home = env::var_os(plugin_bridge::BRIDGE_HOST_HOME_ENV);
    let host_workspace = env::var_os(plugin_bridge::BRIDGE_HOST_WORKSPACE_ENV);
    match (host_home, host_workspace) {
        (None, None) => Ok(()),
        (Some(home), Some(workspace)) => {
            let home = PathBuf::from(home);
            require_bridge_home(cli.home.as_deref(), &home)?;
            validate_bridge_host_command(
                &cli.command,
                &PathBuf::from(workspace),
                &env::current_dir()?,
            )
        }
        _ => bail!("brgr Herdr bridge host context is incomplete"),
    }
}

pub(crate) fn require_bridge_home(requested: Option<&Path>, expected: &Path) -> Result<()> {
    if requested != Some(expected) {
        bail!("brgr Herdr bridge cannot override its control home");
    }
    Ok(())
}

pub(crate) fn validate_bridge_host_command(
    command: &Command,
    workspace_root: &Path,
    current_dir: &Path,
) -> Result<()> {
    let workspace_root = workspace_root
        .canonicalize()
        .context("brgr Herdr bridge workspace is unavailable")?;
    match command {
        Command::Run(args) => {
            if args.workflow.is_some() {
                bail!(
                    "this brgr command is unavailable through the Herdr host bridge; run it explicitly outside the plugin Codex pane"
                );
            }
            require_bridge_workspace(
                args.workspace.as_deref().unwrap_or(current_dir),
                &workspace_root,
            )?;
            Ok(())
        }
        Command::Revise(args) => {
            if let Some(workspace) = args.workspace.as_deref() {
                require_bridge_workspace(workspace, &workspace_root)?;
            }
            Ok(())
        }
        // Turning on issue filing publishes text from this machine; turning on
        // auto-recertify lets paid scratch runs start without a command; a
        // handoff condition steers a head toward starting another harness.
        Command::Config {
            command:
                ConfigCommand::SetIssueReporting { .. }
                | ConfigCommand::SetAutoRecertify { .. }
                | ConfigCommand::SetHandoffCondition { .. },
        } => bail!(
            "this brgr command is unavailable through the Herdr host bridge; run it explicitly outside the plugin Codex pane"
        ),
        Command::Status { .. }
        | Command::Result { .. }
        | Command::Diff { .. }
        | Command::Wait { .. }
        | Command::Watch { .. }
        | Command::Message { .. }
        | Command::Debate { .. }
        | Command::Input { .. }
        | Command::Report { .. }
        | Command::Cancel { .. }
        | Command::Bind { .. }
        | Command::Head { .. }
        | Command::Accept { .. }
        | Command::Reject { .. }
        | Command::Doctor
        | Command::Errors {
            command: None | Some(ErrorsCommand::List | ErrorsCommand::Preview),
        }
        | Command::Config { .. }
        | Command::Cleanup { .. }
        | Command::Harness {
            command: HarnessCommand::Status { .. },
        }
        | Command::Supervise { .. } => Ok(()),
        Command::Artifact {
            command: ArtifactCommand::Export { output, .. },
        } => require_bridge_workspace(output.parent().unwrap_or(current_dir), &workspace_root),
        Command::Apply { workspace, .. } | Command::Land { workspace, .. } => {
            require_bridge_workspace(workspace, &workspace_root)
        }
        // `workflow init` writes SDK files and does not touch owner state, the
        // store, or a worker. Same confinement as artifact export: the target
        // directory must already exist inside the selected workspace.
        Command::Workflow {
            command: crate::workflow::WorkflowCommand::Init { dir, .. },
        } => require_bridge_workspace(dir.as_deref().unwrap_or(current_dir), &workspace_root),
        Command::Workflow { .. }
        | Command::Harness { .. }
        | Command::Integrate { .. }
        | Command::Prune { .. }
        | Command::Errors {
            command: Some(ErrorsCommand::File),
        } => bail!(
            "this brgr command is unavailable through the Herdr host bridge; run it explicitly outside the plugin Codex pane"
        ),
        Command::Plugin { .. }
        | Command::Hook { .. }
        | Command::Context { .. }
        | Command::Notify { .. }
        | Command::NotifyWorkflow { .. }
        | Command::PaneRun(_)
        | Command::TuiHost { .. }
        | Command::Session { .. }
        | Command::SealReport { .. }
        | Command::NativeResult { .. }
        | Command::OmpRun { .. } => {
            bail!("internal brgr commands are unavailable through the Herdr host bridge")
        }
    }
}

pub(crate) fn require_bridge_workspace(candidate: &Path, workspace_root: &Path) -> Result<()> {
    let candidate = candidate
        .canonicalize()
        .context("brgr Herdr bridge task workspace is unavailable")?;
    if !candidate.starts_with(workspace_root) {
        bail!("brgr Herdr bridge task workspace is outside the selected Herdr workspace");
    }
    Ok(())
}