feat(mesh): add /mesh trace and /mesh stats REPL commands
- /mesh trace <address> - show route to a mesh address (stub, needs MeshRouter integration) - /mesh stats - show delivery statistics per destination (stub) - /mesh store now shows actual message count from P2pNode when active - Updated help text with new commands
This commit is contained in:
@@ -70,6 +70,8 @@ pub(crate) enum SlashCommand {
|
||||
MeshRoute,
|
||||
MeshIdentity,
|
||||
MeshStore,
|
||||
MeshTrace { address: String },
|
||||
MeshStats,
|
||||
/// Display safety number for out-of-band key verification with a contact.
|
||||
Verify { username: String },
|
||||
/// Rotate own MLS leaf key in the active group.
|
||||
@@ -220,12 +222,22 @@ pub(crate) fn parse_input(line: &str) -> Input {
|
||||
Input::Slash(SlashCommand::MeshSubscribe { topic: topic.into() })
|
||||
}
|
||||
}
|
||||
Some("route") => Input::Slash(SlashCommand::MeshRoute),
|
||||
Some("route") | Some("routes") => Input::Slash(SlashCommand::MeshRoute),
|
||||
Some("identity") | Some("id") => Input::Slash(SlashCommand::MeshIdentity),
|
||||
Some("store") => Input::Slash(SlashCommand::MeshStore),
|
||||
Some("stats") => Input::Slash(SlashCommand::MeshStats),
|
||||
Some(rest) if rest.starts_with("trace ") => {
|
||||
let address = rest[6..].trim();
|
||||
if address.is_empty() {
|
||||
display::print_error("usage: /mesh trace <address>");
|
||||
Input::Empty
|
||||
} else {
|
||||
Input::Slash(SlashCommand::MeshTrace { address: address.into() })
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
display::print_error(
|
||||
"usage: /mesh start|stop|peers|server|send|broadcast|subscribe|route|identity|store"
|
||||
"usage: /mesh start|stop|peers|server|send|broadcast|subscribe|route|identity|store|trace|stats"
|
||||
);
|
||||
Input::Empty
|
||||
}
|
||||
@@ -823,6 +835,8 @@ async fn handle_slash(
|
||||
SlashCommand::MeshRoute => cmd_mesh_route(session),
|
||||
SlashCommand::MeshIdentity => cmd_mesh_identity(session),
|
||||
SlashCommand::MeshStore => cmd_mesh_store(session),
|
||||
SlashCommand::MeshTrace { address } => cmd_mesh_trace(session, &address),
|
||||
SlashCommand::MeshStats => cmd_mesh_stats(session),
|
||||
SlashCommand::Verify { username } => cmd_verify(session, client, &username).await,
|
||||
SlashCommand::UpdateKey => cmd_update_key(session, client).await,
|
||||
SlashCommand::Typing => cmd_typing(session, client).await,
|
||||
@@ -878,6 +892,8 @@ pub(crate) fn print_help() {
|
||||
display::print_status(" /mesh route - Show known mesh peers and routes");
|
||||
display::print_status(" /mesh identity - Show mesh node identity info");
|
||||
display::print_status(" /mesh store - Show mesh store-and-forward stats");
|
||||
display::print_status(" /mesh trace <address> - Show route to a mesh address");
|
||||
display::print_status(" /mesh stats - Show delivery statistics per destination");
|
||||
display::print_status(" /update-key - Rotate your MLS leaf key in the active group");
|
||||
display::print_status(" /verify <username> - Show safety number for key verification");
|
||||
display::print_status(" /react <emoji> [index] - React to last message (or message at index)");
|
||||
@@ -1390,10 +1406,74 @@ pub(crate) fn cmd_mesh_identity(session: &SessionState) -> anyhow::Result<()> {
|
||||
pub(crate) fn cmd_mesh_store(session: &SessionState) -> anyhow::Result<()> {
|
||||
#[cfg(feature = "mesh")]
|
||||
{
|
||||
// Without a live P2pNode in the session, we can only report that the store
|
||||
// is not active. Once P2pNode is wired in, this will show real stats.
|
||||
display::print_status("mesh store: not active (P2P node not started in this session)");
|
||||
display::print_status("start mesh mode to enable store-and-forward");
|
||||
match &session.p2p_node {
|
||||
Some(node) => {
|
||||
let store = node.mesh_store();
|
||||
let guard = store.lock().map_err(|e| anyhow::anyhow!("store lock: {e}"))?;
|
||||
let (total_messages, unique_recipients) = guard.stats();
|
||||
display::print_status(&format!("mesh store: {} messages for {} recipients", total_messages, unique_recipients));
|
||||
}
|
||||
None => {
|
||||
display::print_status("mesh store: not active (P2P node not started)");
|
||||
display::print_status("use /mesh start to enable store-and-forward");
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "mesh"))]
|
||||
{
|
||||
let _ = session;
|
||||
display::print_error("requires --features mesh");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Show route to a mesh address.
|
||||
pub(crate) fn cmd_mesh_trace(session: &SessionState, address: &str) -> anyhow::Result<()> {
|
||||
#[cfg(feature = "mesh")]
|
||||
{
|
||||
// Parse the address (hex string to 16 bytes)
|
||||
let addr_bytes = match hex::decode(address) {
|
||||
Ok(b) if b.len() == 16 => {
|
||||
let mut arr = [0u8; 16];
|
||||
arr.copy_from_slice(&b);
|
||||
arr
|
||||
}
|
||||
Ok(b) if b.len() == 32 => {
|
||||
// Full public key — compute truncated address
|
||||
quicprochat_p2p::announce::compute_address(&b)
|
||||
}
|
||||
_ => {
|
||||
display::print_error("invalid address: expected 16-byte hex (32 chars) or 32-byte key (64 chars)");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
display::print_status(&format!("tracing route to {}", hex::encode(addr_bytes)));
|
||||
|
||||
// For now, show the route from the routing table if we had one
|
||||
// In a full implementation, this would query the MeshRouter
|
||||
display::print_status(" (routing table not yet wired to REPL session)");
|
||||
display::print_status(" this will show hop-by-hop path once MeshRouter is integrated");
|
||||
|
||||
let _ = session;
|
||||
}
|
||||
#[cfg(not(feature = "mesh"))]
|
||||
{
|
||||
let _ = (session, address);
|
||||
display::print_error("requires --features mesh");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Show delivery statistics per destination.
|
||||
pub(crate) fn cmd_mesh_stats(session: &SessionState) -> anyhow::Result<()> {
|
||||
#[cfg(feature = "mesh")]
|
||||
{
|
||||
// For now, report that stats are not available without MeshRouter
|
||||
display::print_status("mesh delivery statistics:");
|
||||
display::print_status(" (MeshRouter not yet wired to REPL session)");
|
||||
display::print_status(" stats will show per-destination delivery counts once integrated");
|
||||
|
||||
let _ = session;
|
||||
}
|
||||
#[cfg(not(feature = "mesh"))]
|
||||
|
||||
Reference in New Issue
Block a user