agent-bridle-tool-shell 0.7.10

Capability-confined shell tool for agent-bridle (argv + safe-subset engine).
Documentation
//! Dispatch-capable host binary for the carried-coreutils integration test
//! (issue #206). Its `main` calls [`maybe_dispatch`] first, so the brush engine's
//! carried coreutils shims — which re-exec `<self> --invoke-bundled <name>` —
//! resolve **in-process against THIS binary**. Run it with the environment
//! scrubbed (`env_clear`) to prove carried `ls`/`cat` work with no host tools.
//!
//! Usage: `dispatch_host <cmd> [cwd]` — runs `<cmd>` through the confined brush
//! engine under full-access, prints captured stdout/stderr, exits with the
//! command's code.

use agent_bridle_core::{Caveats, Gate, Tool};
use agent_bridle_tool_shell::{maybe_dispatch, BrushShellTool};

fn main() {
    // MUST be first: if invoked as `<self> --invoke-bundled <name> …`, run the
    // carried coreutil in-process (a fresh post-exec process — safe) and exit.
    if let Some(code) = maybe_dispatch() {
        std::process::exit(code);
    }

    let mut args = std::env::args().skip(1);
    let cmd = args.next().expect("usage: dispatch_host <cmd> [cwd]");
    let cwd = args.next();

    // Benchmark/diagnosis seam: raise the engine's wall-clock ceiling so a
    // measurement can distinguish "slow" from "deadlocked" (a run that never
    // completes) instead of always reporting the default 60s timeout.
    let tool = match std::env::var("AB_BRUSH_TIMEOUT_SECS")
        .ok()
        .and_then(|v| v.parse::<u64>().ok())
    {
        Some(secs) => BrushShellTool::new().with_timeout(std::time::Duration::from_secs(secs)),
        None => BrushShellTool::new(),
    };
    let ctx = Gate::new(0)
        .authorize(&tool, &Caveats::top())
        .expect("authorize");

    let mut json = serde_json::json!({ "cmd": cmd });
    if let Some(cwd) = cwd {
        json["cwd"] = serde_json::Value::String(cwd);
    }

    let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
    let out = rt
        .block_on(async { tool.invoke(json, &ctx).await })
        .expect("invoke");

    if let Some(s) = out.get("stdout").and_then(|v| v.as_str()) {
        print!("{s}");
    }
    if let Some(s) = out.get("stderr").and_then(|v| v.as_str()) {
        eprint!("{s}");
    }
    let code = out
        .get("exit_code")
        .and_then(serde_json::Value::as_i64)
        .unwrap_or(1);
    std::process::exit(code as i32);
}