matrix: hive-matrix-mcp crate (daemon+stdio bridge) + harness wiring (#548 phase 3)
This commit is contained in:
parent
23ed124881
commit
e6c53045ad
16 changed files with 3310 additions and 35 deletions
86
hive-matrix-mcp/src/main.rs
Normal file
86
hive-matrix-mcp/src/main.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
//! `hive-matrix-daemon` binary — long-running matrix-sdk Client + sync
|
||||
//! loop per agent. Bridges incoming room events to hyperhive wake
|
||||
//! signals and serves the unix socket the stdio MCP bridge talks to.
|
||||
//!
|
||||
//! Lifecycle:
|
||||
//! 1. Read access token from `paths::token_file()` (fail clean if absent).
|
||||
//! 2. Whoami probe → recover `user_id` + `device_id` → restore
|
||||
//! matrix-sdk session (no login flow).
|
||||
//! 3. Install the message-event handler that fires hyperhive wakes.
|
||||
//! 4. Spawn the unix socket listener for the MCP bridge.
|
||||
//! 5. Run sync forever.
|
||||
//!
|
||||
//! Standalone-degraded boot: missing token file → exit 0 cleanly so
|
||||
//! systemd's `ConditionPathExists=` doesn't have to be perfectly
|
||||
//! synced with hive-c0re's token-provisioning timing.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use matrix_sdk::config::SyncSettings;
|
||||
|
||||
mod client;
|
||||
mod handlers;
|
||||
mod paths;
|
||||
mod protocol;
|
||||
mod socket;
|
||||
mod timeline;
|
||||
mod wake;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
|
||||
)
|
||||
.with_writer(std::io::stderr)
|
||||
.init();
|
||||
|
||||
let homeserver = paths::homeserver_url();
|
||||
let token_file = paths::token_file();
|
||||
let state_dir = paths::matrix_state_dir();
|
||||
let mcp_socket = paths::daemon_socket();
|
||||
let hyperhive_socket = paths::hyperhive_socket();
|
||||
|
||||
if !tokio::fs::try_exists(&token_file).await.unwrap_or(false) {
|
||||
tracing::warn!(
|
||||
path = %token_file.display(),
|
||||
"matrix token file absent; exiting cleanly (hive-c0re will provision \
|
||||
it on first agent registration, then systemd restarts us)"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
homeserver,
|
||||
token_file = %token_file.display(),
|
||||
state_dir = %state_dir.display(),
|
||||
"hive-matrix-daemon starting"
|
||||
);
|
||||
|
||||
let matrix_client = client::build_and_restore(&homeserver, &token_file, &state_dir)
|
||||
.await
|
||||
.context("build matrix client")?;
|
||||
|
||||
timeline::install_message_handler(&matrix_client, hyperhive_socket);
|
||||
|
||||
// Spawn the unix socket server before sync starts so the MCP
|
||||
// bridge can connect as soon as the first claude turn fires. The
|
||||
// socket dispatches against the same `Client` we sync on, so any
|
||||
// tool call benefits from the sync state-cache.
|
||||
let socket_client = matrix_client.clone();
|
||||
let socket_listener = mcp_socket.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = socket::serve(&socket_listener, socket_client).await {
|
||||
tracing::error!(error = %e, "mcp socket server exited");
|
||||
}
|
||||
});
|
||||
|
||||
// Sync forever; matrix-sdk handles reconnection internally.
|
||||
let sync_settings = SyncSettings::default();
|
||||
matrix_client
|
||||
.sync(sync_settings)
|
||||
.await
|
||||
.context("matrix-sdk sync loop exited")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Reference in a new issue