From b6fe452ea794185ff61af8f040070edb3ffe6685 Mon Sep 17 00:00:00 2001 From: kaichao Date: Sat, 4 Jul 2026 11:59:44 +0800 Subject: [PATCH] feat: add config for logos chat client (#163) * feat: add config for logos chat client * chore: fix mock registry log message * chore: set custom fields in logos config --- .github/scripts/mock-registry.py | 2 +- bin/chat-cli/src/main.rs | 33 +++++++------ crates/client/src/config.rs | 3 ++ crates/client/src/lib.rs | 4 +- crates/client/src/logos.rs | 80 +++++++++++++++++++++++--------- 5 files changed, 82 insertions(+), 40 deletions(-) diff --git a/.github/scripts/mock-registry.py b/.github/scripts/mock-registry.py index 034cc46..17c5226 100644 --- a/.github/scripts/mock-registry.py +++ b/.github/scripts/mock-registry.py @@ -38,7 +38,7 @@ class Handler(BaseHTTPRequestHandler): self._drain() self._reply(404) - def log_message(self, *args): + def log_message(self, format, *args): pass diff --git a/bin/chat-cli/src/main.rs b/bin/chat-cli/src/main.rs index cfb5f34..3e8b43b 100644 --- a/bin/chat-cli/src/main.rs +++ b/bin/chat-cli/src/main.rs @@ -11,8 +11,8 @@ use crossbeam_channel::Receiver; use logos_account::TestLogosAccount; use logos_chat::{ AccountDirectory, ChatClient, ChatClientBuilder, ChatStore, DelegateSigner, Event, - HttpRegistry, LogosChatClient, NETWORK_PRESET, REGISTRY_ENDPOINT, RegistrationService, - StorageConfig, Transport, + HttpRegistry, LogosChatClient, LogosConfig, NETWORK_PRESET, REGISTRY_ENDPOINT, + RegistrationService, StorageConfig, Transport, }; use app::ChatApp; @@ -49,9 +49,10 @@ struct Cli { #[arg(long)] preset: Option, - /// TCP port for the embedded logos-delivery node. - #[arg(long, default_value_t = 60000)] - port: u16, + /// TCP port for the embedded logos-delivery node. When omitted, the + /// preconfigured port is used. + #[arg(long)] + port: Option, /// Write logs to a file instead of stderr (keeps TUI output clean). #[arg(long)] @@ -84,15 +85,19 @@ fn main() -> Result<()> { println!("Starting logos-delivery node (preset={preset})..."); println!("This may take a few seconds while connecting to the network."); - let (client, events) = LogosChatClient::open( - db_str, - "chat-cli", - cli.port, - cli.preset.as_deref(), - cli.registry_url.as_deref(), - ) - .map_err(|e| anyhow::anyhow!("{e:?}")) - .context("failed to open chat client")?; + let mut config = LogosConfig::new(db_str, "chat-cli"); + if let Some(port) = cli.port { + config.set_tcp_port(port); + } + if let Some(preset) = cli.preset.as_deref() { + config.set_preset(preset); + } + if let Some(registry_url) = cli.registry_url.as_deref() { + config.set_registry_url(registry_url); + } + let (client, events) = LogosChatClient::open(config) + .map_err(|e| anyhow::anyhow!("{e:?}")) + .context("failed to open chat client")?; println!("Node connected."); launch_tui(client, events, &cli) diff --git a/crates/client/src/config.rs b/crates/client/src/config.rs index 4200971..1918e8b 100644 --- a/crates/client/src/config.rs +++ b/crates/client/src/config.rs @@ -5,3 +5,6 @@ pub const REGISTRY_ENDPOINT: &str = "https://devnet.chat-kc.logos.co"; /// The logos-delivery network preset the Logos client joins by default. pub const NETWORK_PRESET: &str = "logos.dev"; + +/// Default TCP port for the embedded logos-delivery node. +pub const DEFAULT_TCP_PORT: u16 = 60000; diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 3319397..b613995 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -10,13 +10,13 @@ mod logos; pub use builder::{ChatClientBuilder, Unset}; pub use client::{ChatClient, Transport}; -pub use config::{NETWORK_PRESET, REGISTRY_ENDPOINT}; +pub use config::{DEFAULT_TCP_PORT, NETWORK_PRESET, REGISTRY_ENDPOINT}; pub use delegate::DelegateSigner; pub use delivery_in_process::{InProcessDelivery, MessageBus}; pub use errors::ClientError; pub use event::{Event, MessageSender}; #[cfg(feature = "embedded-p2p-delivery")] -pub use logos::LogosChatClient; +pub use logos::{LogosChatClient, LogosConfig}; // Re-export types callers need to interact with ChatClient. pub use libchat::{ diff --git a/crates/client/src/logos.rs b/crates/client/src/logos.rs index 2fd34fb..4634a7b 100644 --- a/crates/client/src/logos.rs +++ b/crates/client/src/logos.rs @@ -20,7 +20,7 @@ use logos_account::TestLogosAccount; use crate::ChatClientBuilder; use crate::client::{ChatClient, Transport}; -use crate::config::{NETWORK_PRESET, REGISTRY_ENDPOINT}; +use crate::config::{DEFAULT_TCP_PORT, NETWORK_PRESET, REGISTRY_ENDPOINT}; use crate::delegate::DelegateSigner; use crate::errors::ClientError; use crate::event::Event; @@ -34,6 +34,54 @@ impl Transport for EmbeddedP2pDeliveryService { } } +/// Configuration for opening a [`LogosChatClient`]. +/// +/// `db_path` (a per-client location) and `db_key` (a secret) are required and +/// never baked into the library. The TCP port and the network config default to +/// the baked-in Logos values; override them with the setters (e.g. to point at a +/// local deployment). +pub struct LogosConfig { + db_path: String, + db_key: String, + tcp_port: u16, + preset: String, + registry_url: String, +} + +impl LogosConfig { + /// Config for the required per-client `db_path` and `db_key`. The TCP port, + /// network preset, and registry endpoint default to the baked-in Logos + /// values; override them with [`set_tcp_port`](Self::set_tcp_port), + /// [`set_preset`](Self::set_preset), and + /// [`set_registry_url`](Self::set_registry_url). + pub fn new(db_path: impl Into, db_key: impl Into) -> Self { + Self { + db_path: db_path.into(), + db_key: db_key.into(), + tcp_port: DEFAULT_TCP_PORT, + preset: NETWORK_PRESET.to_string(), + registry_url: REGISTRY_ENDPOINT.to_string(), + } + } + + /// Override the TCP port for the embedded logos-delivery node. + pub fn set_tcp_port(&mut self, tcp_port: u16) { + self.tcp_port = tcp_port; + } + + /// Override the logos-delivery network preset (defaults to the baked-in + /// [`NETWORK_PRESET`]). + pub fn set_preset(&mut self, preset: impl Into) { + self.preset = preset.into(); + } + + /// Override the registry endpoint (account + keypackage store; defaults to + /// the baked-in [`REGISTRY_ENDPOINT`]). + pub fn set_registry_url(&mut self, registry_url: impl Into) { + self.registry_url = registry_url.into(); + } +} + /// A [`ChatClient`] wired to the Logos service stack: a [`DelegateSigner`] /// identity acting for a fresh dev account, the HTTP keypackage + account /// registry ([`HttpRegistry`], which is both the keypackage store and the @@ -42,37 +90,23 @@ impl Transport for EmbeddedP2pDeliveryService { pub type LogosChatClient = ChatClient; impl LogosChatClient { - /// Open a client on the Logos stack, starting a logos-delivery node on - /// `tcp_port` as its transport and persisting to the encrypted database at - /// `db_path` unlocked with `db_key`. When `preset`/`registry_url` are `Some`, - /// they override the baked-in network preset/registry endpoint (e.g. a local - /// deployment); otherwise the preconfigured values are used. - /// - /// `db_path` is a per-client location, `db_key` is a secret, and `tcp_port` - /// is a per-client local resource, so all three are caller-supplied — never - /// baked into the library. - pub fn open( - db_path: impl Into, - db_key: impl Into, - tcp_port: u16, - preset: Option<&str>, - registry_url: Option<&str>, - ) -> Result<(Self, Receiver), ClientError> { + /// Open a client on the Logos stack per `config`, starting a logos-delivery + /// node as its transport and persisting to the encrypted database. + pub fn open(config: LogosConfig) -> Result<(Self, Receiver), ClientError> { let transport = EmbeddedP2pDeliveryService::start(P2pConfig { - preset: preset.unwrap_or(NETWORK_PRESET).to_string(), - tcp_port, + preset: config.preset, + tcp_port: config.tcp_port, ..Default::default() }) .map_err(|e| ClientError::Transport(e.to_string()))?; - let endpoint = registry_url.unwrap_or(REGISTRY_ENDPOINT); // A fresh account endorsing a fresh delegate each open: the account // key is dropped after publishing the bundle, so devices cannot be // added later. A caller-supplied, custody-holding account replaces // this once the platform provides one. let account = TestLogosAccount::new(); let delegate = DelegateSigner::random(); - let mut registry = HttpRegistry::new(endpoint); + let mut registry = HttpRegistry::new(config.registry_url); account .add_delegate_signer(&mut registry, delegate.public_key()) .map_err(|e| ClientError::BundlePublish(e.to_string()))?; @@ -81,8 +115,8 @@ impl LogosChatClient { .transport(transport) .registration(registry) .storage_config(StorageConfig::Encrypted { - path: db_path.into(), - key: db_key.into(), + path: config.db_path, + key: config.db_key, }) .build() }