feat: segundo módulo (nakui) + admin API + brahman-status
Dos cosas en una sesión, en el orden discutido:
(1) Segundo módulo brahman vivo: nakui-core
- crates/modules/nakui/core/Cargo.toml: deps brahman-card,
brahman-sidecar, ulid.
- crates/modules/nakui/core/src/bin/nakui.rs: brahman_card_for_nakui()
construye una Card como Lifecycle::Daemon, Supervision::Restart,
flow.input "command" (json) + flow.output "report" (json). El
cmd_run llama brahman_sidecar::spawn antes de levantar el server
de nakui.
(2) crates/shared/brahman-sidecar (estrena crates/shared/)
Boilerplate del sidecar extraído (DRY): el thread con tokio current
thread runtime, conexión vía Client::connect, ping loop. Yahweh y
nakui ahora consumen este crate. API:
- spawn(card) fire-and-forget
- spawn_with_handle(config) con JoinHandle
Example "presence" útil para demos: módulo dummy con label tomado
del primer arg que se queda vivo hasta SIGTERM.
(3) crates/core/brahman-admin: observabilidad del broker
Socket Unix paralelo en \$BRAHMAN_ADMIN_SOCKET (default
\$XDG_RUNTIME_DIR/brahman-admin.sock). Cada conexión recibe un
StatusSnapshot JSON line-delimited y se cierra. Compatible con nc/socat.
- StatusSnapshot { server, protocol, init_attached, sessions, matches }
- server::AdminServer
- client::query(path)
- example "brahman-status" CLI
(4) Wiring de ente-zero
En primordial_loop, junto al handshake server, ahora también levanta
AdminServer con misma política de degradación grácil.
(5) brahman-broker: BrokeredCard ahora incluye lifecycle. Endpoint y
Match derivan Serialize/Deserialize. Nuevo método cards() expone
iterador de BrokeredCard para que el admin pueda construir snapshots.
(6) brahman-card: re-export pub use ulid::* para que módulos no
necesiten depender de ulid directamente.
(7) yahweh-shell migrado al sidecar compartido. Su brahman_client.rs
pasa de 96 a 53 líneas: sólo declara la Card, delega el spawn.
Demo end-to-end:
$ ente-zero &
$ presence demo.producer &
$ presence demo.consumer &
$ brahman-status
Init: server=0.1.0 protocol=0.1.0 attached=true
Sessions (2):
01KR42TY1J... demo.producer lifecycle=Daemon priority=Normal
01KR42TY1K... demo.consumer lifecycle=Daemon priority=Normal
Matches (2):
demo.producer.in ← demo.consumer.out via Exact
demo.consumer.in ← demo.producer.out via Exact
El broker matchea bidireccional por tipo. El admin lo expone.
Tests: 27/27. cargo check --workspace: 0 errores.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,7 @@ notify = { workspace = true }
|
||||
|
||||
# Brahman protocol — sidecar thread que se presenta al Init.
|
||||
brahman-card = { path = "../../core/brahman-card" }
|
||||
brahman-handshake = { path = "../../core/brahman-handshake" }
|
||||
brahman-sidecar = { path = "../../shared/brahman-sidecar" }
|
||||
ulid = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
|
||||
@@ -1,84 +1,19 @@
|
||||
//! Sidecar brahman: yahweh se presenta al Init como módulo `Widget`.
|
||||
//! Card de yahweh-shell + spawn del sidecar brahman compartido.
|
||||
//!
|
||||
//! Vive en un thread aparte con tokio runtime current_thread, desacoplado
|
||||
//! de GPUI. Si el Init no está disponible, loggea y termina — yahweh
|
||||
//! sigue funcionando standalone. Si conecta, mantiene la sesión viva
|
||||
//! con pings periódicos hasta que la GUI termine o el server caiga.
|
||||
//!
|
||||
//! Card declarada:
|
||||
//! - label: `brahman.ui_engine`
|
||||
//! - lifecycle: `Widget`
|
||||
//! - flow.input: `render-data` (json)
|
||||
//! - flow.output: `user-intent` (json)
|
||||
//! - permissions: filesystem read-write (yahweh persiste `layout.json`),
|
||||
//! IPC `wit-v1`.
|
||||
//! La lógica de thread + tokio + ping-loop vive en `brahman-sidecar`;
|
||||
//! aquí sólo declaramos la identidad de yahweh como módulo Widget.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::time::Duration;
|
||||
|
||||
use brahman_card::{
|
||||
Card, Flow, Flows, FsPolicy, IpcPolicy, Lifecycle, Payload, Permissions, Priority, Supervision,
|
||||
TypeRef, CARD_SCHEMA_VERSION,
|
||||
};
|
||||
use brahman_handshake::{client::Client, transport};
|
||||
use ulid::Ulid;
|
||||
|
||||
/// Período entre pings al Init.
|
||||
const PING_INTERVAL: Duration = Duration::from_secs(30);
|
||||
|
||||
/// Spawn del sidecar brahman. No-op si el thread no se puede crear.
|
||||
/// Devuelve inmediatamente; la conexión se establece en background.
|
||||
/// Spawn del sidecar con la Card de yahweh.
|
||||
pub fn spawn() {
|
||||
let result = std::thread::Builder::new()
|
||||
.name("brahman-client".into())
|
||||
.spawn(run_thread);
|
||||
if let Err(e) = result {
|
||||
eprintln!("[brahman] no se pudo spawnear el sidecar: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
fn run_thread() {
|
||||
let rt = match tokio::runtime::Builder::new_current_thread()
|
||||
.enable_io()
|
||||
.enable_time()
|
||||
.build()
|
||||
{
|
||||
Ok(rt) => rt,
|
||||
Err(e) => {
|
||||
eprintln!("[brahman] tokio runtime falló: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
rt.block_on(run_client());
|
||||
}
|
||||
|
||||
async fn run_client() {
|
||||
let path = transport::default_socket_path();
|
||||
let card = build_card();
|
||||
|
||||
let mut client = match Client::connect(&path, card).await {
|
||||
Ok(c) => {
|
||||
eprintln!(
|
||||
"[brahman] attached: session={} init_attached={} server={}",
|
||||
c.session(),
|
||||
c.server_info().init_attached,
|
||||
c.server_info().server_version
|
||||
);
|
||||
c
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[brahman] no conectado a {} ({e})", path.display());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
tokio::time::sleep(PING_INTERVAL).await;
|
||||
if let Err(e) = client.ping().await {
|
||||
eprintln!("[brahman] ping falló: {e}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
brahman_sidecar::spawn(build_card());
|
||||
}
|
||||
|
||||
fn build_card() -> Card {
|
||||
|
||||
Reference in New Issue
Block a user