use crate::core::event::{Event, SessionRecord};
use crate::store::{SessionFilter, SessionPage, SpanNode};
use serde::{Deserialize, Serialize};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
pub const PROTO_VERSION: u32 = 1;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ClientKind {
Tui,
Cli,
Mcp,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientHello {
pub proto_version: u32,
pub client: ClientKind,
pub workspace: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerHello {
pub proto_version: u32,
pub daemon_version: String,
pub workspaces: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DaemonStatus {
pub pid: u32,
pub uptime_ms: u64,
pub queue_depth: usize,
pub last_error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionDetail {
pub session: Option<SessionRecord>,
pub events: Vec<Event>,
pub spans: Vec<SpanNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum DaemonRequest {
Hello(ClientHello),
Status,
Stop,
ListSessions {
workspace: String,
offset: usize,
limit: usize,
filter: SessionFilter,
},
GetSessionDetail {
id: String,
workspace: Option<String>,
},
IngestHook {
source: crate::shell::ingest::IngestSource,
payload: String,
workspace: Option<String>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum DaemonResponse {
Hello(ServerHello),
Status(DaemonStatus),
Sessions(SessionPage),
Detail(Box<SessionDetail>),
Ack {
message: String,
},
Error {
message: String,
supported_min: Option<u32>,
supported_max: Option<u32>,
},
}
pub async fn read_frame<T, R>(reader: &mut R) -> anyhow::Result<T>
where
T: for<'de> Deserialize<'de>,
R: AsyncRead + Unpin,
{
let len = reader.read_u32().await? as usize;
let mut buf = vec![0_u8; len];
reader.read_exact(&mut buf).await?;
Ok(serde_json::from_slice(&buf)?)
}
pub async fn write_frame<T, W>(writer: &mut W, value: &T) -> anyhow::Result<()>
where
T: Serialize,
W: AsyncWrite + Unpin,
{
let buf = serde_json::to_vec(value)?;
writer.write_u32(buf.len() as u32).await?;
writer.write_all(&buf).await?;
writer.flush().await?;
Ok(())
}