first
This commit is contained in:
90
agent/src/terminal/recv.rs
Normal file
90
agent/src/terminal/recv.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
// 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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user