poc-daemon: subscribe to channel notifications, drop config.rs

Wire poc-daemon into channel daemon notifications via subscribe_all().
Channel notifications (IRC, telegram, tmux) now flow through the
existing notification pipeline instead of the dead module system.

Remove claude/config.rs — daemon config is fully covered by
channel config files in ~/.consciousness/channels/.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2026-04-05 12:58:46 -04:00
parent 1941624249
commit 8c1fef3c69
5 changed files with 22 additions and 180 deletions

View file

@ -1,97 +0,0 @@
// Daemon configuration.
//
// Lives at ~/.consciousness/daemon.toml. Loaded on startup, updated at
// runtime when modules change state (join channel, etc.).
use crate::thalamus::home;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
fn config_path() -> PathBuf {
home().join(".consciousness/daemon.toml")
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Config {
#[serde(default)]
pub irc: IrcConfig,
#[serde(default)]
pub telegram: TelegramConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IrcConfig {
pub enabled: bool,
pub server: String,
pub port: u16,
pub tls: bool,
pub nick: String,
pub user: String,
pub realname: String,
pub channels: Vec<String>,
}
impl Default for IrcConfig {
fn default() -> Self {
Self {
enabled: true,
server: "irc.libera.chat".into(),
port: 6697,
tls: true,
nick: "agent".into(),
user: "agent".into(),
realname: "agent".into(),
channels: vec!["#bcachefs".into(), "#bcachefs-ai".into()],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TelegramConfig {
pub enabled: bool,
pub token: String,
pub chat_id: i64,
}
impl Default for TelegramConfig {
fn default() -> Self {
// Load token and chat_id from legacy files if they exist
let token = std::fs::read_to_string(home().join(".consciousness/telegram/token"))
.map(|s| s.trim().to_string())
.unwrap_or_default();
let chat_id = std::fs::read_to_string(home().join(".consciousness/telegram/chat_id"))
.ok()
.and_then(|s| s.trim().parse().ok())
.unwrap_or(0);
Self {
enabled: !token.is_empty() && chat_id != 0,
token,
chat_id,
}
}
}
impl Config {
pub fn load() -> Self {
let path = config_path();
match fs::read_to_string(&path) {
Ok(data) => toml::from_str(&data).unwrap_or_else(|e| {
log::warn!("bad config {}: {e}, using defaults", path.display());
Self::default()
}),
Err(_) => {
let config = Self::default();
config.save();
config
}
}
}
pub fn save(&self) {
let path = config_path();
if let Ok(data) = toml::to_string_pretty(self) {
let _ = fs::write(path, data);
}
}
}

View file

@ -7,7 +7,6 @@
// The daemon protocol (daemon_capnp) and universal infrastructure
// (channels, supervisor, notify) remain in thalamus/.
pub mod config;
pub mod context;
pub mod hook;
pub mod idle;
@ -443,7 +442,6 @@ async fn server_main() -> Result<(), Box<dyn std::error::Error>> {
let pid = std::process::id();
std::fs::write(pid_path(), pid.to_string()).ok();
let daemon_config = Rc::new(RefCell::new(config::Config::load()));
let state = Rc::new(RefCell::new(idle::State::new()));
state.borrow_mut().load();
@ -452,13 +450,22 @@ async fn server_main() -> Result<(), Box<dyn std::error::Error>> {
tokio::task::LocalSet::new()
.run_until(async move {
// Start modules
let (_notify_tx, mut notify_rx) = tokio::sync::mpsc::unbounded_channel::<notify::Notification>();
// External modules (IRC, Telegram) now run as separate daemons.
// They connect via the notification channel when implemented.
let _irc_state: Option<()> = None;
let _telegram_state: Option<()> = None;
// Subscribe to channel daemon notifications
let (notify_tx, mut notify_rx) = tokio::sync::mpsc::unbounded_channel::<notify::Notification>();
{
let channel_rx = crate::thalamus::channels::subscribe_all();
let tx = notify_tx.clone();
std::thread::spawn(move || {
while let Ok(cn) = channel_rx.recv() {
let _ = tx.send(notify::Notification {
ntype: cn.channel,
urgency: cn.urgency,
message: cn.preview,
timestamp: crate::thalamus::now(),
});
}
});
}
let listener = UnixListener::bind(&sock)?;
#[cfg(unix)]
@ -528,7 +535,6 @@ async fn server_main() -> Result<(), Box<dyn std::error::Error>> {
let daemon_impl = rpc::DaemonImpl::new(
state.clone(),
daemon_config.clone(),
);
let client: daemon_capnp::daemon::Client =
capnp_rpc::new_client(daemon_impl);

View file

@ -4,7 +4,6 @@
// notify::NotifyState, and module state. All state is owned by
// RefCells on the LocalSet — no Send/Sync needed.
use super::config::Config;
use super::idle;
use crate::thalamus::{daemon_capnp, notify};
use daemon_capnp::daemon;
@ -15,16 +14,11 @@ use log::info;
pub struct DaemonImpl {
state: Rc<RefCell<idle::State>>,
// TODO: replace with named channel map
_config: Rc<RefCell<Config>>,
}
impl DaemonImpl {
pub fn new(
state: Rc<RefCell<idle::State>>,
_config: Rc<RefCell<Config>>,
) -> Self {
Self { state, _config }
pub fn new(state: Rc<RefCell<idle::State>>) -> Self {
Self { state }
}
}