haematite-cli 0.21.3

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
use super::{
    approval_watch::{Answer, Expected},
    permission::Decision,
};
use serde_json::Value;

pub(super) fn draft(
    value: &Value,
    id: &str,
    expected: &Expected,
    agent: &str,
    store: &str,
) -> anyhow::Result<Option<Answer>> {
    anyhow::ensure!(
        value["id"] == id
            && value["holder"] == agent
            && value["request_digest"] == expected.digest
            && value["target"]["kind"] == "haematite.database"
            && value["target"]["id"] == store
            && value["target"]["action"] == "agent_permission",
        "permission_draft_binding_differs"
    );
    let decision = match value["state"].as_str() {
        Some("waiting") => return Ok(None),
        Some("approved") => Decision::Approve,
        Some("refused" | "refused_on_execution") => Decision::Refuse,
        Some("executed") => Decision::Cancelled,
        _ => anyhow::bail!("permission_draft_state_invalid"),
    };
    let person = value["decided_by"]
        .as_str()
        .filter(|person| !person.is_empty() && person.len() <= 128 && *person != agent)
        .ok_or_else(|| anyhow::anyhow!("permission_draft_decider_invalid"))?;
    anyhow::ensure!(
        value["decided_at"].as_u64().is_some_and(|time| time > 0),
        "permission_draft_decision_time_invalid"
    );
    Ok(Some(Answer {
        draft: id.to_owned(),
        decision,
        person: person.to_owned(),
        failure: None,
    }))
}