feat(brahman-handshake): ListSessions endpoint + cliente + UI broker-explorer
Iter 20. Nuevo flujo end-to-end para observabilidad: cualquier módulo
conectado puede pedir al broker la lista de sesiones activas y mostrar
labels + flows in/out por cada una.
brahman-handshake/messages:
- Frame::ListSessions(ListSessions{session}) → Frame::SessionList(SessionList{entries}).
- SessionEntry: session, label, schema_version, outputs, inputs, conscious.
brahman-handshake/server:
- run_post_handshake pasa SessionRegistry a handle_inbound_frame.
- build_session_list helper proyecta el snapshot bajo lock.
- Validación session_id mismatched → Unauthorized.
brahman-handshake/client:
- Client::list_sessions() async, drena MatchEvents intermedios al
pending_events buffer, mismo patrón que ping().
brahman-sidecar/discovery:
- list_sessions / list_sessions_blocking arman Card observer mínima,
piden, Farewell.
brahman-broker-explorer:
- Poll-tick agrega list_sessions_blocking cuando broker está UP*.
- stat_card "Sesiones activas" con count + items ordenados por Ulid:
label · in:[flows] out:[flows] (wit)?.
Test list_sessions_returns_currently_registered: 3 clientes
conectados, observer pide list, verifica labels + schema_version
+ conscious=false. 24 handshake tests + sidecar + broker-explorer
verde.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -162,6 +162,16 @@ where
|
||||
got: "MatchEvent (pre-handshake)",
|
||||
});
|
||||
}
|
||||
Frame::ListSessions(_) => {
|
||||
return Err(ClientError::UnexpectedFrame {
|
||||
got: "ListSessions (pre-handshake)",
|
||||
});
|
||||
}
|
||||
Frame::SessionList(_) => {
|
||||
return Err(ClientError::UnexpectedFrame {
|
||||
got: "SessionList (pre-handshake)",
|
||||
});
|
||||
}
|
||||
};
|
||||
Ok(Self {
|
||||
stream,
|
||||
@@ -227,6 +237,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Pide al servidor el listado de sesiones activas. Pensado para
|
||||
/// observadores (broker-explorer, CLIs de diagnóstico). Como
|
||||
/// `ping`, los `MatchEvent` que lleguen intercalados se bufean
|
||||
/// en `pending_events` y no rompen la respuesta.
|
||||
pub async fn list_sessions(&mut self) -> Result<crate::messages::SessionList, ClientError> {
|
||||
write_frame(
|
||||
&mut self.stream,
|
||||
&Frame::ListSessions(crate::messages::ListSessions {
|
||||
session: self.session,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
loop {
|
||||
match read_frame(&mut self.stream).await? {
|
||||
Frame::SessionList(list) => return Ok(list),
|
||||
Frame::MatchEvent(ev) => self.pending_events.push_back(ev),
|
||||
Frame::Error(e) => return Err(ClientError::Server(e)),
|
||||
_ => {
|
||||
return Err(ClientError::UnexpectedFrame {
|
||||
got: "non-session-list",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Cierre cooperativo. Consume el cliente.
|
||||
pub async fn farewell(mut self) -> Result<(), ClientError> {
|
||||
write_frame(
|
||||
|
||||
Reference in New Issue
Block a user