feat(tui): add usage overlay and sidebar for displaying usage statistics and tasks

feat(tui): implement status bar with connection and turn state indicators
feat(tui): create workflow panel for agent status and progress visualization
feat(web): introduce web frontend interface with static file serving
feat(ws): add WebSocket interface for real-time communication and session management
This commit is contained in:
asepharyana
2026-07-20 09:04:57 +07:00
parent bceba665c0
commit da2ed6da25
454 changed files with 13979 additions and 29539 deletions
+63
View File
@@ -0,0 +1,63 @@
//! Daemon server — owns the agent state, listens on a per-session Unix
//! socket, and drives one attached client at a time.
//!
//! Flow: `run_daemon()` creates a session + lock → binds a Unix socket
//! under `<store>/run/<session_id>.sock` → blocks for a single client to
//! `accept()` → loops reading `ClientRequest`s, translating each into
//! `Action`(s) via the same `handle_key`/`apply_action` path the
//! single-process mode uses, then pushes a full state update back →
//! on `Close` or client disconnect, cleans up the socket file, saves
//! settings, and releases the lock.
//!
//! Why: reuses `crate::handler::handle_key` by synthesising a
//! `crossterm::KeyEvent` from the IPC `KeyAction`, so daemon and
//! single-process modes share identical key-handling logic.
use anyhow::Result;
use zesdex_infrastructure::ipc::server::IpcServer;
use crate::handler::handle_daemon_client;
use crate::state::create_session;
/// Run zesdex as a background daemon: owns the agent state, listens on a
/// per-session Unix socket, and drives one attached client.
///
/// Flow: create session + lock it → bind a Unix socket under
/// `<store>/run/<session_id>.sock` → block for a single client to
/// `accept()` → loop reading `ClientRequest`s, translating each into
/// `Action`(s) → on `Close` or client disconnect, clean up the socket file,
/// save settings, and release the lock.
pub fn run_daemon() -> Result<()> {
tracing::info!("starting daemon process");
let (store, _session_lock_guard, mut state, _rt) = create_session()?;
let run_dir = store.base_dir.join("run");
std::fs::create_dir_all(&run_dir)?;
let socket_path = run_dir.join(format!("{}.sock", state.session_id));
let addr = socket_path.to_string_lossy().to_string();
let server = IpcServer::bind_unix(&addr)?;
eprintln!("daemon: listening on {addr}");
loop {
let conn = match server.accept() {
Ok(c) => c,
Err(e) => {
eprintln!("daemon: accept error: {e}");
break;
}
};
eprintln!("daemon: client connected");
if let Err(e) = handle_daemon_client(conn, &mut state) {
eprintln!("daemon: error handling client: {e}");
}
eprintln!("daemon: client disconnected, waiting for next connection...");
state.save_settings();
}
let _ = std::fs::remove_file(&socket_path);
Ok(())
}