haematite-cli 0.21.8

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
use std::path::Path;
use tokio::process::Command;

pub(super) fn home(path: &Path) -> anyhow::Result<()> {
    use std::os::unix::fs::{MetadataExt as _, OpenOptionsExt as _, PermissionsExt as _};
    anyhow::ensure!(
        !path.as_os_str().is_empty(),
        "harness_home_required: harness.home is required"
    );
    anyhow::ensure!(
        path.is_absolute(),
        "harness_home_must_be_absolute: harness.home must be absolute"
    );
    let file = std::fs::OpenOptions::new()
        .read(true)
        .custom_flags(i32::try_from(
            (rustix::fs::OFlags::NOFOLLOW
                | rustix::fs::OFlags::DIRECTORY
                | rustix::fs::OFlags::NONBLOCK)
                .bits(),
        )?)
        .open(path)
        .map_err(|error| {
            anyhow::anyhow!(
                "harness_home_unreadable: harness.home cannot be opened: {}",
                error.kind()
            )
        })?;
    let metadata = file.metadata().map_err(|error| {
        anyhow::anyhow!(
            "harness_home_unreadable: harness.home metadata failed: {}",
            error.kind()
        )
    })?;
    anyhow::ensure!(
        metadata.is_dir()
            && metadata.uid() == rustix::process::geteuid().as_raw()
            && metadata.permissions().mode().trailing_zeros() >= 6,
        "harness_home_not_private: harness.home must be a directory owned by this caller with no group or other access"
    );
    Ok(())
}

const INHERITED: &[&str] = &[
    "PATH",
    "HOME",
    "TMPDIR",
    "SHELL",
    "USER",
    "LOGNAME",
    "LANG",
    "LC_ALL",
    "TERM",
    "XDG_CONFIG_HOME",
    "XDG_DATA_HOME",
    "XDG_CACHE_HOME",
    "XDG_STATE_HOME",
    "HTTP_PROXY",
    "HTTPS_PROXY",
    "NO_PROXY",
    "http_proxy",
    "https_proxy",
    "no_proxy",
    "SSL_CERT_FILE",
    "SSL_CERT_DIR",
    "NODE_EXTRA_CA_CERTS",
    "TOKENIZERS_PARALLELISM",
];
// Account and proxy names are the launch renderer's explicit model inputs.
const MODEL_ACCOUNT: &[&str] = &[
    "ANTHROPIC_AUTH_TOKEN",
    "CODEX_API_KEY",
    "LYS_ACCOUNT_VARIABLE",
    "ANTHROPIC_BASE_URL",
    "OPENAI_BASE_URL",
];
const DECLARED_PROVIDER: &[&str] = &[
    "ANTHROPIC_API_KEY",
    "OPENAI_API_KEY",
    "CLAUDE_CODE_OAUTH_TOKEN",
];

pub(super) fn check_provider(names: &[String]) -> anyhow::Result<()> {
    for name in DECLARED_PROVIDER {
        anyhow::ensure!(
            std::env::var_os(name).is_none_or(|value| value.is_empty())
                || names.iter().any(|declared| declared == name),
            "provider_environment_undeclared: {name}"
        );
    }
    Ok(())
}

pub(super) fn validate(names: &[String]) -> anyhow::Result<()> {
    for name in names {
        let upper = name.to_ascii_uppercase();
        anyhow::ensure!(
            !name.is_empty()
                && name.len() <= 128
                && name
                    .bytes()
                    .all(|byte| byte.is_ascii_alphanumeric() || byte == b'_'),
            "environment_name_invalid"
        );
        anyhow::ensure!(
            DECLARED_PROVIDER.contains(&name.as_str())
                || (!["KEY", "TOKEN", "SECRET", "PASSWORD", "CREDENTIAL"]
                    .iter()
                    .any(|part| upper.contains(part))
                    && !["_AUTH", "_PASS", "_PAT", "DATABASE_URL"]
                        .iter()
                        .any(|part| upper.ends_with(part))),
            "credential_environment_refused: {name}"
        );
    }
    Ok(())
}

pub(super) fn apply(command: &mut Command, declared: &[String]) -> anyhow::Result<()> {
    validate(declared)?;
    check_provider(declared)?;
    command.env_clear();
    for name in INHERITED.iter().chain(MODEL_ACCOUNT) {
        if let Some(value) = std::env::var_os(name) {
            command.env(name, value);
        }
    }
    for name in declared {
        let value = std::env::var_os(name)
            .ok_or_else(|| anyhow::anyhow!("environment_declared_but_absent: {name}"))?;
        command.env(name, value);
    }
    command.env_remove("LYS_AGENT_PASS");
    Ok(())
}

pub(super) fn harness_home(
    command: &mut Command,
    harness: &super::config::Harness,
) -> anyhow::Result<()> {
    home(&harness.home)?;
    command
        .env_remove("CODEX_HOME")
        .env_remove("CLAUDE_CONFIG_DIR");
    match harness.id.as_str() {
        "codex" => {
            command.env("CODEX_HOME", &harness.home);
        }
        "claude-code" => {
            command.env("CLAUDE_CONFIG_DIR", &harness.home);
        }
        _ => {}
    }
    Ok(())
}