agent: protocol.rs — WsMessage types matching server, builder helpers
This commit is contained in:
parent
50e5df07c0
commit
56f6e88389
162
agent/src/protocol.rs
Normal file
162
agent/src/protocol.rs
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
//! Message types for communication between the agent and the Butterfly server.
|
||||||
|
//!
|
||||||
|
//! These mirror the server's `WsMessage` enum (serde-tagged with `msg_type`).
|
||||||
|
//! The agent sends **Agent → Server** messages and receives **Server → Agent** messages.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
// ── Agent → Server ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// A single display frame, base64-encoded JPEG.
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct DisplayFrame {
|
||||||
|
pub session_id: String,
|
||||||
|
pub data: String,
|
||||||
|
pub timestamp: Option<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An audio chunk (future: PCM/Opus encoded).
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct AudioFrame {
|
||||||
|
pub session_id: String,
|
||||||
|
pub data: String,
|
||||||
|
pub timestamp: Option<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Agent announces its capabilities on connect.
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct AgentInfo {
|
||||||
|
pub session_id: String,
|
||||||
|
pub agent_id: String,
|
||||||
|
pub resolution: Option<String>,
|
||||||
|
pub hostname: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Keep-alive ping.
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct Heartbeat;
|
||||||
|
|
||||||
|
// ── Server → Agent ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// A HUD command forwarded from a viewer (mouse, keyboard, etc.).
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct ForwardHudCommand {
|
||||||
|
pub command: String,
|
||||||
|
pub params: serde_json::Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A resize request forwarded from a viewer.
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct ForwardResize {
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Server tells the agent to start/stop streaming.
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct StreamControl {
|
||||||
|
pub action: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic acknowledgment from server.
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Ack {
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error from server.
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct ErrorMsg {
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Unified message envelope ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// All messages use a `msg_type` discriminator for routing.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "msg_type", rename_all = "snake_case")]
|
||||||
|
pub enum AgentWsMessage {
|
||||||
|
// Outgoing (Agent → Server)
|
||||||
|
#[serde(rename = "display_frame")]
|
||||||
|
DisplayFrame {
|
||||||
|
session_id: String,
|
||||||
|
data: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
timestamp: Option<f64>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "audio_frame")]
|
||||||
|
AudioFrame {
|
||||||
|
session_id: String,
|
||||||
|
data: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
timestamp: Option<f64>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "agent_info")]
|
||||||
|
AgentInfo {
|
||||||
|
session_id: String,
|
||||||
|
agent_id: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
resolution: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
hostname: Option<String>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "heartbeat")]
|
||||||
|
Heartbeat,
|
||||||
|
|
||||||
|
// Incoming (Server → Agent) — included for deserialization
|
||||||
|
#[serde(rename = "forward_hud_command")]
|
||||||
|
ForwardHudCommand {
|
||||||
|
command: String,
|
||||||
|
params: serde_json::Value,
|
||||||
|
},
|
||||||
|
#[serde(rename = "forward_resize")]
|
||||||
|
ForwardResize {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
},
|
||||||
|
#[serde(rename = "stream_control")]
|
||||||
|
StreamControl { action: String },
|
||||||
|
#[serde(rename = "ack")]
|
||||||
|
Ack { message: String },
|
||||||
|
#[serde(rename = "error")]
|
||||||
|
Error { message: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Build a display frame message.
|
||||||
|
pub fn display_frame_msg(session_id: &str, data: String) -> String {
|
||||||
|
let msg = AgentWsMessage::DisplayFrame {
|
||||||
|
session_id: session_id.to_string(),
|
||||||
|
data,
|
||||||
|
timestamp: None,
|
||||||
|
};
|
||||||
|
serde_json::to_string(&msg).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build an audio frame message.
|
||||||
|
pub fn audio_frame_msg(session_id: &str, data: String) -> String {
|
||||||
|
let msg = AgentWsMessage::AudioFrame {
|
||||||
|
session_id: session_id.to_string(),
|
||||||
|
data,
|
||||||
|
timestamp: None,
|
||||||
|
};
|
||||||
|
serde_json::to_string(&msg).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build an agent info message.
|
||||||
|
pub fn agent_info_msg(session_id: &str, agent_id: &str, resolution: Option<&str>, hostname: Option<&str>) -> String {
|
||||||
|
let msg = AgentWsMessage::AgentInfo {
|
||||||
|
session_id: session_id.to_string(),
|
||||||
|
agent_id: agent_id.to_string(),
|
||||||
|
resolution: resolution.map(String::from),
|
||||||
|
hostname: hostname.map(String::from),
|
||||||
|
};
|
||||||
|
serde_json::to_string(&msg).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a heartbeat message.
|
||||||
|
pub fn heartbeat_msg() -> String {
|
||||||
|
let msg = AgentWsMessage::Heartbeat;
|
||||||
|
serde_json::to_string(&msg).unwrap_or_default()
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user