91 lines
2.5 KiB
Rust
91 lines
2.5 KiB
Rust
// Created by: Claude
|
|
// Date: 2026-01-04
|
|
// Purpose: Receive terminal output from QUIC stream
|
|
// Refs: protocol_events_v_2.md, AGENT.md
|
|
|
|
use crate::p2p::protocol::TerminalMessage;
|
|
use quinn::{RecvStream, SendStream};
|
|
use anyhow::Result;
|
|
use tracing::info;
|
|
use tokio::io::AsyncReadExt;
|
|
|
|
pub struct TerminalReceiver {
|
|
has_control: bool,
|
|
}
|
|
|
|
impl TerminalReceiver {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
has_control: false,
|
|
}
|
|
}
|
|
|
|
/// Receive and display terminal output
|
|
pub async fn receive_output<F>(&self, mut stream: RecvStream, mut on_output: F) -> Result<()>
|
|
where
|
|
F: FnMut(String),
|
|
{
|
|
loop {
|
|
let msg = self.receive_message(&mut stream).await?;
|
|
|
|
match msg {
|
|
TerminalMessage::Output { data } => {
|
|
on_output(data);
|
|
}
|
|
_ => {
|
|
info!("Received terminal message: {:?}", msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Send input to remote terminal (if has_control)
|
|
pub async fn send_input(&self, stream: &mut SendStream, data: String) -> Result<()> {
|
|
if !self.has_control {
|
|
anyhow::bail!("Cannot send input: no control capability");
|
|
}
|
|
|
|
let msg = TerminalMessage::Input { data };
|
|
self.send_message(stream, &msg).await
|
|
}
|
|
|
|
/// Send resize command
|
|
pub async fn send_resize(&self, stream: &mut SendStream, cols: u16, rows: u16) -> Result<()> {
|
|
let msg = TerminalMessage::Resize { cols, rows };
|
|
self.send_message(stream, &msg).await
|
|
}
|
|
|
|
pub fn grant_control(&mut self) {
|
|
info!("Terminal control granted");
|
|
self.has_control = true;
|
|
}
|
|
|
|
pub fn revoke_control(&mut self) {
|
|
info!("Terminal control revoked");
|
|
self.has_control = false;
|
|
}
|
|
|
|
async fn receive_message(&self, stream: &mut RecvStream) -> Result<TerminalMessage> {
|
|
let mut len_buf = [0u8; 4];
|
|
stream.read_exact(&mut len_buf).await?;
|
|
let len = u32::from_be_bytes(len_buf) as usize;
|
|
|
|
let mut buf = vec![0u8; len];
|
|
stream.read_exact(&mut buf).await?;
|
|
|
|
Ok(serde_json::from_slice(&buf)?)
|
|
}
|
|
|
|
async fn send_message(&self, stream: &mut SendStream, msg: &TerminalMessage) -> Result<()> {
|
|
use tokio::io::AsyncWriteExt;
|
|
|
|
let json = serde_json::to_vec(msg)?;
|
|
let len = (json.len() as u32).to_be_bytes();
|
|
|
|
stream.write_all(&len).await?;
|
|
stream.write_all(&json).await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|