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::{config::Seat, tools::Identity};
use crate::{
    client::{Client, Ended, Reply},
    mcp::adapter::{Sender, ServiceRefusal},
    serve::{cli::Config, protocol::Operation},
};
use serde::Deserialize;
use serde_json::{Value, json, value::RawValue};
use std::{path::PathBuf, time::Duration};

pub(super) struct Native {
    pub page: u32,
    socket: PathBuf,
    timeout: Duration,
    issuer: String,
    agent: String,
    proof: String,
    bound: usize,
}
#[derive(Deserialize)]
struct Wire {
    method: String,
    params: Option<Box<RawValue>>,
}
impl Native {
    pub fn new(seat: &Seat, identity: &Identity) -> anyhow::Result<Self> {
        let config: Config = serde_json::from_slice(&std::fs::read(&seat.serve_config)?)
            .map_err(|_| anyhow::anyhow!("assistant_serve_config_invalid"))?;
        let endpoint = config.endpoint(false)?;
        anyhow::ensure!(endpoint.name == seat.store, "assistant_serve_store_differs");
        Ok(Self {
            page: config.max_page.get(),
            socket: endpoint.socket,
            timeout: Duration::from_millis(config.timeout_ms.get()),
            issuer: seat.issuer.clone(),
            agent: identity.agent.clone(),
            proof: identity.launch.clone(),
            bound: seat.harness.issuer_response_bytes,
        })
    }
    pub fn read(&mut self, method: &str, params: &Value) -> Result<Value, ServiceRefusal> {
        let request = serde_json::from_value(json!({"method":method,"params":params}))
            .map_err(|error| encoding(&error))?;
        serde_json::from_str(self.send(request)?.get()).map_err(|error| encoding(&error))
    }
}
impl Sender for Native {
    fn send(&mut self, request: Operation) -> Result<Box<RawValue>, ServiceRefusal> {
        let pass = super::tools::acquire(&self.issuer, &self.agent, &self.proof, self.bound)
            .map_err(|_| ServiceRefusal {
                code: "assistant_record_pass_refused".to_owned(),
                message: "could not acquire the agent's record pass".to_owned(),
                receipt: None,
            })?;
        let mut client = Client::connect(&self.socket, self.timeout).map_err(ended)?;
        let raw = serde_json::value::to_raw_value(&json!({"pass":pass}))
            .map_err(|error| encoding(&error))?;
        answered(client.call("session.pass", Some(&raw)).map_err(ended)?)?;
        let raw = serde_json::value::to_raw_value(&request).map_err(|error| encoding(&error))?;
        let operation: Wire = serde_json::from_str(raw.get()).map_err(|error| encoding(&error))?;
        answered(
            client
                .call(&operation.method, operation.params.as_deref())
                .map_err(ended)?,
        )
    }
}
fn answered(reply: Reply) -> Result<Box<RawValue>, ServiceRefusal> {
    match reply {
        Reply::Answered(raw) => Ok(raw),
        Reply::Refused(error) => Err(ServiceRefusal {
            code: error.code,
            message: error.message,
            receipt: error.verified.map(|receipt| receipt.raw().to_owned()),
        }),
    }
}
fn encoding(error: &serde_json::Error) -> ServiceRefusal {
    ServiceRefusal {
        code: "assistant_record_encoding_failed".to_owned(),
        message: error.to_string(),
        receipt: None,
    }
}
fn ended(error: Ended) -> ServiceRefusal {
    let (code, message, receipt) = match error {
        Ended::Usage(why) | Ended::NotAsked(why) => ("assistant_record_not_asked", why, None),
        Ended::Unknown(why) => ("assistant_record_outcome_unknown", why, None),
        Ended::Received { why, receipt } => (
            "assistant_record_outcome_unknown",
            why,
            Some(receipt.raw().to_owned()),
        ),
    };
    ServiceRefusal {
        code: code.to_owned(),
        message,
        receipt,
    }
}