feat: Phase D-3 + D-4 — service_socket en Card, providers coexisten
Cierra el ciclo del swap automático Nous mock↔real:
- brahman-card: Card.service_socket: Option<PathBuf> y espejo en
WireCard. Path del data plane (distinto al Init). Cualquier
consumer que matchee con esta Card conecta directo, sin discovery
extra.
- brahman-broker: BrokeredCard propaga service_socket. Sin
participación en matching — sólo metadata.
- brahman-handshake::MatchEvent: nuevo campo
producer_service_socket. Server lo busca en BrokeredCard al emitir
Available.
- nouser-nous::transport: provider_socket_path(provider: &str)
devuelve nouser-nous-{provider}.sock por default. Mock y real
coexisten en sockets distintos (Phase D-4). default_socket_path()
conserva el comportamiento single-provider.
- Mock declara nouser-nous-mock.sock; real declara
nouser-nous-real.sock. La Card se construye DESPUÉS del bind.
- brahman-status imprime "socket:" por sesión cuando está presente.
Validación end-to-end:
$ ente-zero & nouser-nous-mock & nouser-nous-real &
$ ls /run/user/1001/nouser-nous-*.sock
nouser-nous-mock.sock
nouser-nous-real.sock
$ brahman-status
Sessions (2):
[ente] nouser.nous_real
socket: /run/user/1001/nouser-nous-real.sock
[ente] nouser.nous_mock
socket: /run/user/1001/nouser-nous-mock.sock
Pendiente (no crítico): nouser-core attract --remote usa todavía
NOUSER_NOUS_SOCKET hardcoded. Siguiente paso: subscribirse al
MatchEvent del broker y usar producer_service_socket directo, así
BRAHMAN_BROKER_CONTEXT=test/prod swapea provider sin tocar al
consumer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,57 @@ ratio/diff ver `git show <sha>`.
|
|||||||
|
|
||||||
## 2026-05-08
|
## 2026-05-08
|
||||||
|
|
||||||
|
### feat: Phase D-3 + D-4 — service_socket en Card, providers coexisten
|
||||||
|
Cierra el ciclo del swap automático de Nous (mock↔real):
|
||||||
|
|
||||||
|
- **Schema** (`brahman-card`): `Card.service_socket: Option<PathBuf>` y
|
||||||
|
espejo en `WireCard`. Conversiones `From` propagan. Es el path del
|
||||||
|
**data plane** (distinto del socket del Init); cualquier consumer
|
||||||
|
que matchee con esta Card puede conectar directo sin discovery
|
||||||
|
adicional.
|
||||||
|
- **Broker** (`brahman-broker`): `BrokeredCard` propaga
|
||||||
|
`service_socket` desde la Card. Sin participación en el matching —
|
||||||
|
sólo metadata para los observadores.
|
||||||
|
- **MatchEvent** (`brahman-handshake`): nuevo campo
|
||||||
|
`producer_service_socket: Option<PathBuf>`. Cuando el server emite
|
||||||
|
`Available`, busca la `BrokeredCard` del productor en el broker y
|
||||||
|
copia su `service_socket`. El consumer recibe la ruta completa para
|
||||||
|
conectar.
|
||||||
|
- **Transport** (`nouser-nous`): `provider_socket_path(provider: &str)`
|
||||||
|
devuelve `nouser-nous-{provider}.sock` por default — mock y real
|
||||||
|
coexisten en sockets distintos (Phase D-4). `default_socket_path()`
|
||||||
|
conserva el comportamiento single-provider.
|
||||||
|
- **Providers**: mock declara `service_socket =
|
||||||
|
/run/user/X/nouser-nous-mock.sock`; real declara
|
||||||
|
`nouser-nous-real.sock`. La Card se construye DESPUÉS del bind para
|
||||||
|
que el path declarado sea el real.
|
||||||
|
- **Status**: `brahman-status` imprime `socket:` por sesión cuando
|
||||||
|
está presente.
|
||||||
|
|
||||||
|
Validación end-to-end:
|
||||||
|
|
||||||
|
$ ente-zero & nouser-nous-mock & nouser-nous-real &
|
||||||
|
$ ls /run/user/1001/nouser-nous-*.sock
|
||||||
|
nouser-nous-mock.sock
|
||||||
|
nouser-nous-real.sock
|
||||||
|
|
||||||
|
$ brahman-status
|
||||||
|
Sessions (2):
|
||||||
|
[ente] ... nouser.nous_real
|
||||||
|
socket: /run/user/1001/nouser-nous-real.sock
|
||||||
|
in embed-request: Primitive { name: "json" }
|
||||||
|
out embed-result: Primitive { name: "json" }
|
||||||
|
[ente] ... nouser.nous_mock
|
||||||
|
socket: /run/user/1001/nouser-nous-mock.sock
|
||||||
|
in embed-request, out embed-result
|
||||||
|
|
||||||
|
Pendientes para futuro (no críticos):
|
||||||
|
- nouser-core attract --remote todavía usa NOUSER_NOUS_SOCKET hardcoded
|
||||||
|
o `default_socket_path()`. El siguiente paso es subscribirse al
|
||||||
|
MatchEvent del broker y usar `producer_service_socket` directo —
|
||||||
|
con eso `BRAHMAN_BROKER_CONTEXT=test/prod` swapea provider sin
|
||||||
|
tocar al consumer.
|
||||||
|
|
||||||
### refactor(nouser): labels de Mónada con 2 componentes del path
|
### refactor(nouser): labels de Mónada con 2 componentes del path
|
||||||
Resuelve la fricción visual de monorepos donde múltiples Mónadas se
|
Resuelve la fricción visual de monorepos donde múltiples Mónadas se
|
||||||
llamaban "src". Nueva función `label_from_path` toma los últimos hasta
|
llamaban "src". Nueva función `label_from_path` toma los últimos hasta
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
" [{}] {} {}{} lifecycle={:?} priority={:?}",
|
" [{}] {} {}{} lifecycle={:?} priority={:?}",
|
||||||
kind_marker, s.session, s.label, conscious_marker, s.lifecycle, s.priority
|
kind_marker, s.session, s.label, conscious_marker, s.lifecycle, s.priority
|
||||||
);
|
);
|
||||||
|
if let Some(sock) = &s.service_socket {
|
||||||
|
println!(" socket: {}", sock.display());
|
||||||
|
}
|
||||||
if let Some(data) = &s.data {
|
if let Some(data) = &s.data {
|
||||||
if !data.summary.is_empty() {
|
if !data.summary.is_empty() {
|
||||||
println!(" summary: {}", data.summary);
|
println!(" summary: {}", data.summary);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use brahman_card::{
|
use brahman_card::{
|
||||||
Card, CardKind, ContextBias, DataFacet, Flow, Lifecycle, Priority, TypeRef, WitInterface,
|
Card, CardKind, ContextBias, DataFacet, Flow, Lifecycle, Priority, TypeRef, WitInterface,
|
||||||
@@ -86,6 +87,9 @@ pub struct BrokeredCard {
|
|||||||
/// Faceta de datos cuando `kind != Ente`.
|
/// Faceta de datos cuando `kind != Ente`.
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub data: Option<DataFacet>,
|
pub data: Option<DataFacet>,
|
||||||
|
/// Socket de servicio (data plane) si lo declara la Card.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub service_socket: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BrokeredCard {
|
impl BrokeredCard {
|
||||||
@@ -101,6 +105,7 @@ impl BrokeredCard {
|
|||||||
priority_contexts: card.priority_contexts.clone(),
|
priority_contexts: card.priority_contexts.clone(),
|
||||||
kind: card.kind,
|
kind: card.kind,
|
||||||
data: card.data.clone(),
|
data: card.data.clone(),
|
||||||
|
service_socket: card.service_socket.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -126,6 +126,14 @@ pub struct Card {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub flow: Flows,
|
pub flow: Flows,
|
||||||
|
|
||||||
|
/// Si la entidad expone un socket Unix de servicio (data plane,
|
||||||
|
/// distinto al socket del Init), declara aquí su path. Los
|
||||||
|
/// consumidores que reciban un `MatchEvent` con este Card como
|
||||||
|
/// productor pueden conectar directo al socket sin discovery
|
||||||
|
/// adicional.
|
||||||
|
#[serde(default)]
|
||||||
|
pub service_socket: Option<PathBuf>,
|
||||||
|
|
||||||
/// Naturaleza de la entidad detrás de la Card. Por defecto `Ente`
|
/// Naturaleza de la entidad detrás de la Card. Por defecto `Ente`
|
||||||
/// para mantener compatibilidad con Cards existentes.
|
/// para mantener compatibilidad con Cards existentes.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -176,6 +184,7 @@ impl Default for Card {
|
|||||||
priority: Priority::default(),
|
priority: Priority::default(),
|
||||||
flow: Flows::default(),
|
flow: Flows::default(),
|
||||||
genesis: Vec::new(),
|
genesis: Vec::new(),
|
||||||
|
service_socket: None,
|
||||||
kind: CardKind::default(),
|
kind: CardKind::default(),
|
||||||
data: None,
|
data: None,
|
||||||
priority_contexts: BTreeMap::new(),
|
priority_contexts: BTreeMap::new(),
|
||||||
@@ -803,6 +812,8 @@ pub struct WireCard {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub genesis: Vec<WireCard>,
|
pub genesis: Vec<WireCard>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub service_socket: Option<PathBuf>,
|
||||||
|
#[serde(default)]
|
||||||
pub kind: CardKind,
|
pub kind: CardKind,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub data: Option<DataFacet>,
|
pub data: Option<DataFacet>,
|
||||||
@@ -827,6 +838,7 @@ impl From<Card> for WireCard {
|
|||||||
priority: c.priority,
|
priority: c.priority,
|
||||||
flow: c.flow,
|
flow: c.flow,
|
||||||
genesis: c.genesis.into_iter().map(WireCard::from).collect(),
|
genesis: c.genesis.into_iter().map(WireCard::from).collect(),
|
||||||
|
service_socket: c.service_socket,
|
||||||
kind: c.kind,
|
kind: c.kind,
|
||||||
data: c.data,
|
data: c.data,
|
||||||
priority_contexts: c.priority_contexts,
|
priority_contexts: c.priority_contexts,
|
||||||
@@ -851,6 +863,7 @@ impl From<WireCard> for Card {
|
|||||||
priority: w.priority,
|
priority: w.priority,
|
||||||
flow: w.flow,
|
flow: w.flow,
|
||||||
genesis: w.genesis.into_iter().map(Card::from).collect(),
|
genesis: w.genesis.into_iter().map(Card::from).collect(),
|
||||||
|
service_socket: w.service_socket,
|
||||||
kind: w.kind,
|
kind: w.kind,
|
||||||
data: w.data,
|
data: w.data,
|
||||||
priority_contexts: w.priority_contexts,
|
priority_contexts: w.priority_contexts,
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
//!
|
//!
|
||||||
//! Todos los mensajes que cruzan el wire son variantes de [`Frame`].
|
//! Todos los mensajes que cruzan el wire son variantes de [`Frame`].
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use brahman_broker::MatchStrategy;
|
use brahman_broker::MatchStrategy;
|
||||||
use brahman_card::{TypeRef, WireCard, WitInterface};
|
use brahman_card::{TypeRef, WireCard, WitInterface};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -102,6 +104,12 @@ pub struct MatchEvent {
|
|||||||
pub via: MatchStrategy,
|
pub via: MatchStrategy,
|
||||||
/// `true` si fue resuelto por `pin_to`.
|
/// `true` si fue resuelto por `pin_to`.
|
||||||
pub pinned: bool,
|
pub pinned: bool,
|
||||||
|
/// Socket de servicio (data plane) que declaró el productor.
|
||||||
|
/// Si está presente, el consumer puede conectar directo sin
|
||||||
|
/// pasar por discovery adicional. `None` si el productor no
|
||||||
|
/// declaró service_socket en su Card.
|
||||||
|
#[serde(default)]
|
||||||
|
pub producer_service_socket: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -297,6 +297,13 @@ impl Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(m) = &new_match {
|
if let Some(m) = &new_match {
|
||||||
|
// Resolvemos el service_socket del productor desde
|
||||||
|
// la BrokeredCard; pasarlo en el evento permite al
|
||||||
|
// consumer conectar directo sin discovery extra.
|
||||||
|
let producer_service_socket = b
|
||||||
|
.cards()
|
||||||
|
.find(|c| c.session == m.producer.session)
|
||||||
|
.and_then(|c| c.service_socket.clone());
|
||||||
let event = MatchEvent {
|
let event = MatchEvent {
|
||||||
kind: MatchEventKind::Available,
|
kind: MatchEventKind::Available,
|
||||||
consumer_flow: input.name.clone(),
|
consumer_flow: input.name.clone(),
|
||||||
@@ -306,6 +313,7 @@ impl Session {
|
|||||||
ty: m.ty.clone(),
|
ty: m.ty.clone(),
|
||||||
via: m.via,
|
via: m.via,
|
||||||
pinned: m.pinned,
|
pinned: m.pinned,
|
||||||
|
producer_service_socket,
|
||||||
};
|
};
|
||||||
let send_res = tx.try_send(Frame::MatchEvent(event));
|
let send_res = tx.try_send(Frame::MatchEvent(event));
|
||||||
debug!(
|
debug!(
|
||||||
@@ -330,6 +338,7 @@ impl Session {
|
|||||||
ty: input.ty.clone(),
|
ty: input.ty.clone(),
|
||||||
via: brahman_broker::MatchStrategy::Exact,
|
via: brahman_broker::MatchStrategy::Exact,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
|
producer_service_socket: None,
|
||||||
};
|
};
|
||||||
let _ = tx.try_send(Frame::MatchEvent(event));
|
let _ = tx.try_send(Frame::MatchEvent(event));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,13 +45,10 @@ const MODEL_ID: &str = "mock-pseudo-32d";
|
|||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
init_tracing();
|
init_tracing();
|
||||||
|
|
||||||
// 1. Sidecar al brahman-init.
|
// 1. Resolver socket del data-plane ANTES de armar la Card, para
|
||||||
let card = build_card();
|
// declararlo en `Card.service_socket` y que los consumidores lo
|
||||||
info!(label = %card.label, "publicando Card al brahman-init");
|
// descubran vía MatchEvent.
|
||||||
brahman_sidecar::spawn(card);
|
let sock_path = transport::provider_socket_path("mock");
|
||||||
|
|
||||||
// 2. Bind del socket Nous.
|
|
||||||
let sock_path = transport::default_socket_path();
|
|
||||||
if sock_path.exists() {
|
if sock_path.exists() {
|
||||||
std::fs::remove_file(&sock_path)?;
|
std::fs::remove_file(&sock_path)?;
|
||||||
}
|
}
|
||||||
@@ -61,6 +58,11 @@ async fn main() -> std::io::Result<()> {
|
|||||||
let listener = UnixListener::bind(&sock_path)?;
|
let listener = UnixListener::bind(&sock_path)?;
|
||||||
info!(socket = %sock_path.display(), "nouser-nous-mock escuchando");
|
info!(socket = %sock_path.display(), "nouser-nous-mock escuchando");
|
||||||
|
|
||||||
|
// 2. Sidecar al brahman-init con la Card que declara el socket.
|
||||||
|
let card = build_card(sock_path.clone());
|
||||||
|
info!(label = %card.label, "publicando Card al brahman-init");
|
||||||
|
brahman_sidecar::spawn(card);
|
||||||
|
|
||||||
// 3. Accept loop.
|
// 3. Accept loop.
|
||||||
loop {
|
loop {
|
||||||
let (stream, _addr) = listener.accept().await?;
|
let (stream, _addr) = listener.accept().await?;
|
||||||
@@ -84,8 +86,10 @@ fn init_tracing() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Card que el mock anuncia al brahman-init. Es kind=Ente (un proceso),
|
/// Card que el mock anuncia al brahman-init. Es kind=Ente (un proceso),
|
||||||
/// con flujos JSON y bias de prioridad para contexto `test`.
|
/// con flujos JSON, bias de prioridad para contexto `test`, y el socket
|
||||||
fn build_card() -> Card {
|
/// data-plane declarado en `service_socket` (consumidores lo reciben
|
||||||
|
/// directo en el `MatchEvent::Available`).
|
||||||
|
fn build_card(service_socket: std::path::PathBuf) -> Card {
|
||||||
let mut priority_contexts = BTreeMap::new();
|
let mut priority_contexts = BTreeMap::new();
|
||||||
priority_contexts.insert(
|
priority_contexts.insert(
|
||||||
"test".into(),
|
"test".into(),
|
||||||
@@ -105,6 +109,7 @@ fn build_card() -> Card {
|
|||||||
lifecycle: Lifecycle::Daemon,
|
lifecycle: Lifecycle::Daemon,
|
||||||
priority: Priority::Normal,
|
priority: Priority::Normal,
|
||||||
kind: CardKind::Ente,
|
kind: CardKind::Ente,
|
||||||
|
service_socket: Some(service_socket),
|
||||||
flow: Flows {
|
flow: Flows {
|
||||||
input: vec![Flow {
|
input: vec![Flow {
|
||||||
name: FLOW_EMBED_REQUEST.into(),
|
name: FLOW_EMBED_REQUEST.into(),
|
||||||
|
|||||||
@@ -65,13 +65,9 @@ async fn main() -> std::io::Result<()> {
|
|||||||
--features embeddings para activar el modelo)"
|
--features embeddings para activar el modelo)"
|
||||||
);
|
);
|
||||||
|
|
||||||
// 1. Sidecar al brahman-init (mismo patrón que el mock).
|
// 1. Resolver socket del data-plane (default `nouser-nous-real.sock`,
|
||||||
let card = build_card();
|
// distinto del mock para coexistir).
|
||||||
info!(label = %card.label, mode = MODEL_ID, "publicando Card al brahman-init");
|
let sock_path = transport::provider_socket_path("real");
|
||||||
brahman_sidecar::spawn(card);
|
|
||||||
|
|
||||||
// 2. Bind del socket Nous (mismo path que el mock — son swappable).
|
|
||||||
let sock_path = transport::default_socket_path();
|
|
||||||
if sock_path.exists() {
|
if sock_path.exists() {
|
||||||
std::fs::remove_file(&sock_path)?;
|
std::fs::remove_file(&sock_path)?;
|
||||||
}
|
}
|
||||||
@@ -81,6 +77,11 @@ async fn main() -> std::io::Result<()> {
|
|||||||
let listener = UnixListener::bind(&sock_path)?;
|
let listener = UnixListener::bind(&sock_path)?;
|
||||||
info!(socket = %sock_path.display(), "nouser-nous-real escuchando");
|
info!(socket = %sock_path.display(), "nouser-nous-real escuchando");
|
||||||
|
|
||||||
|
// 2. Sidecar al brahman-init con Card declarando el socket.
|
||||||
|
let card = build_card(sock_path.clone());
|
||||||
|
info!(label = %card.label, mode = MODEL_ID, "publicando Card al brahman-init");
|
||||||
|
brahman_sidecar::spawn(card);
|
||||||
|
|
||||||
// 3. Inicializar el modelo (sólo en modo embeddings).
|
// 3. Inicializar el modelo (sólo en modo embeddings).
|
||||||
#[cfg(feature = "embeddings")]
|
#[cfg(feature = "embeddings")]
|
||||||
let backend = embeddings::Backend::init().map_err(|e| {
|
let backend = embeddings::Backend::init().map_err(|e| {
|
||||||
@@ -128,7 +129,8 @@ fn init_tracing() {
|
|||||||
/// Card que real-nous anuncia. Idéntica al mock excepto por:
|
/// Card que real-nous anuncia. Idéntica al mock excepto por:
|
||||||
/// - label distinto (`nouser.nous_real`) para que coexistan en el broker.
|
/// - label distinto (`nouser.nous_real`) para que coexistan en el broker.
|
||||||
/// - `priority_contexts.prod = +1` (gana en contexto prod).
|
/// - `priority_contexts.prod = +1` (gana en contexto prod).
|
||||||
fn build_card() -> Card {
|
/// - `service_socket` propio para que clientes lo descubran directo.
|
||||||
|
fn build_card(service_socket: std::path::PathBuf) -> Card {
|
||||||
let mut priority_contexts = BTreeMap::new();
|
let mut priority_contexts = BTreeMap::new();
|
||||||
priority_contexts.insert(
|
priority_contexts.insert(
|
||||||
"prod".into(),
|
"prod".into(),
|
||||||
@@ -147,6 +149,7 @@ fn build_card() -> Card {
|
|||||||
lifecycle: Lifecycle::Daemon,
|
lifecycle: Lifecycle::Daemon,
|
||||||
priority: Priority::Normal,
|
priority: Priority::Normal,
|
||||||
kind: CardKind::Ente,
|
kind: CardKind::Ente,
|
||||||
|
service_socket: Some(service_socket),
|
||||||
flow: Flows {
|
flow: Flows {
|
||||||
input: vec![Flow {
|
input: vec![Flow {
|
||||||
name: FLOW_EMBED_REQUEST.into(),
|
name: FLOW_EMBED_REQUEST.into(),
|
||||||
|
|||||||
@@ -117,18 +117,32 @@ pub mod transport {
|
|||||||
/// Variable de entorno para sobreescribir la ruta del socket.
|
/// Variable de entorno para sobreescribir la ruta del socket.
|
||||||
pub const SOCKET_ENV: &str = "NOUSER_NOUS_SOCKET";
|
pub const SOCKET_ENV: &str = "NOUSER_NOUS_SOCKET";
|
||||||
|
|
||||||
/// Nombre por default del socket dentro del runtime dir.
|
/// Nombre genérico del socket cuando hay un solo proveedor.
|
||||||
pub const SOCKET_NAME: &str = "nouser-nous.sock";
|
pub const SOCKET_NAME: &str = "nouser-nous.sock";
|
||||||
|
|
||||||
/// Ruta canónica al socket de Nous.
|
/// Ruta canónica al socket cuando un único proveedor está activo
|
||||||
|
/// (consumidores que no quieren elegir).
|
||||||
pub fn default_socket_path() -> PathBuf {
|
pub fn default_socket_path() -> PathBuf {
|
||||||
if let Ok(p) = std::env::var(SOCKET_ENV) {
|
if let Ok(p) = std::env::var(SOCKET_ENV) {
|
||||||
return PathBuf::from(p);
|
return PathBuf::from(p);
|
||||||
}
|
}
|
||||||
let base = std::env::var_os("XDG_RUNTIME_DIR")
|
runtime_base().join(SOCKET_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ruta default para un proveedor identificado (`"mock"`, `"real"`,
|
||||||
|
/// etc). Permite que mock y real coexistan sin clash de socket.
|
||||||
|
/// `NOUSER_NOUS_SOCKET` igual override esta función si está set.
|
||||||
|
pub fn provider_socket_path(provider: &str) -> PathBuf {
|
||||||
|
if let Ok(p) = std::env::var(SOCKET_ENV) {
|
||||||
|
return PathBuf::from(p);
|
||||||
|
}
|
||||||
|
runtime_base().join(format!("nouser-nous-{}.sock", provider))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime_base() -> PathBuf {
|
||||||
|
std::env::var_os("XDG_RUNTIME_DIR")
|
||||||
.map(PathBuf::from)
|
.map(PathBuf::from)
|
||||||
.unwrap_or_else(std::env::temp_dir);
|
.unwrap_or_else(std::env::temp_dir)
|
||||||
base.join(SOCKET_NAME)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user