wire channel list RPC into consciousness F5 screen

fetch_all_channels() connects to each daemon socket and calls
list() via capnp RPC. Runs on a dedicated thread (capnp uses Rc).
Results sent back via mpsc channel, TUI reads cached state.

Fetched at startup and when switching to F5 thalamus screen.
Also calls ensure_running() to restart dead daemons.

Co-Developed-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
ProofOfConcept 2026-04-03 19:53:23 -04:00
parent e7be2a3ba0
commit 8e66f0a66c
4 changed files with 198 additions and 50 deletions

View file

@ -176,3 +176,87 @@ impl ChannelManager {
result
}
}
/// One-shot query: connect to a daemon socket, call list(), return results.
/// Safe to call from any async context (no LocalSet needed).
async fn query_one_daemon(sock: &std::path::Path) -> Vec<(String, bool, u32)> {
let stream = match UnixStream::connect(sock).await {
Ok(s) => s,
Err(_) => return Vec::new(),
};
let (reader, writer) = stream.compat().split();
let rpc_network = Box::new(twoparty::VatNetwork::new(
futures::io::BufReader::new(reader),
futures::io::BufWriter::new(writer),
rpc_twoparty_capnp::Side::Client,
Default::default(),
));
let mut rpc_system = RpcSystem::new(rpc_network, None);
let client: channel_server::Client =
rpc_system.bootstrap(rpc_twoparty_capnp::Side::Server);
let rpc_handle = tokio::task::spawn_local(async move {
let _ = rpc_system.await;
});
let mut result = Vec::new();
if let Ok(reply) = client.list_request().send().promise.await {
if let Ok(r) = reply.get() {
if let Ok(channels) = r.get_channels() {
for ch in channels.iter() {
if let Ok(name) = ch.get_name() {
result.push((
name.to_str().unwrap_or("").to_string(),
ch.get_connected(),
ch.get_unread(),
));
}
}
}
}
}
rpc_handle.abort();
result
}
/// Fetch channel status from all daemon sockets.
/// Runs on a dedicated thread because capnp-rpc uses Rc (not Send).
pub async fn fetch_all_channels() -> Vec<(String, bool, u32)> {
tokio::task::spawn_blocking(|| {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let local = tokio::task::LocalSet::new();
local.block_on(&rt, fetch_all_channels_inner())
})
.await
.unwrap_or_default()
}
async fn fetch_all_channels_inner() -> Vec<(String, bool, u32)> {
let channels_dir = dirs::home_dir()
.unwrap_or_default()
.join(".consciousness/channels");
let mut sup = super::supervisor::Supervisor::new();
sup.load_config();
sup.ensure_running(); // restart any dead daemons
let mut result = Vec::new();
for (daemon_name, _enabled, alive) in sup.status() {
if !alive {
result.push((daemon_name, false, 0));
continue;
}
let sock = channels_dir.join(format!("{}.sock", daemon_name));
let channels = query_one_daemon(&sock).await;
if channels.is_empty() {
result.push((daemon_name, false, 0));
} else {
result.extend(channels);
}
}
result
}