From 040ac99fedc03fa478827b000037dca09b75f991 Mon Sep 17 00:00:00 2001 From: Artem Lytvyn Date: Thu, 16 Jul 2026 14:51:41 +0100 Subject: [PATCH 1/2] fix(server): respect tty flag in ExecSandboxInteractive Signed-off-by: Artem Lytvyn --- crates/openshell-server/src/grpc/sandbox.rs | 23 ++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/crates/openshell-server/src/grpc/sandbox.rs b/crates/openshell-server/src/grpc/sandbox.rs index 8e2f6d25c5..6a3592bc54 100644 --- a/crates/openshell-server/src/grpc/sandbox.rs +++ b/crates/openshell-server/src/grpc/sandbox.rs @@ -1287,6 +1287,7 @@ pub(super) async fn handle_exec_sandbox_interactive( let timeout_seconds = req.timeout_seconds; let cols = if req.cols == 0 { 80 } else { req.cols }; let rows = if req.rows == 0 { 24 } else { req.rows }; + let tty = req.tty; let sandbox_id = sandbox.object_id().to_string(); @@ -1314,6 +1315,7 @@ pub(super) async fn handle_exec_sandbox_interactive( timeout_seconds, cols, rows, + tty, ) .await { @@ -1634,6 +1636,7 @@ async fn stream_interactive_exec_over_relay( timeout_seconds: u32, cols: u32, rows: u32, + tty: bool, ) -> Result<(), Status> { let command_preview: String = command .chars() @@ -1658,6 +1661,7 @@ async fn stream_interactive_exec_over_relay( input_stream, cols, rows, + tty, tx.clone(), ); @@ -1709,6 +1713,7 @@ async fn run_interactive_exec_with_russh( mut input_stream: tonic::Streaming, cols: u32, rows: u32, + tty: bool, tx: mpsc::Sender>, ) -> Result { use openshell_core::proto::exec_sandbox_input::Payload; @@ -1752,10 +1757,12 @@ async fn run_interactive_exec_with_russh( .await .map_err(|e| Status::internal(format!("failed to open ssh channel: {e}")))?; - channel - .request_pty(false, "xterm-256color", cols, rows, 0, 0, &[]) - .await - .map_err(|e| Status::internal(format!("failed to allocate PTY: {e}")))?; + if tty { + channel + .request_pty(false, "xterm-256color", cols, rows, 0, 0, &[]) + .await + .map_err(|e| Status::internal(format!("failed to allocate PTY: {e}")))?; + } channel .exec(true, command.as_bytes()) @@ -1773,9 +1780,11 @@ async fn run_interactive_exec_with_russh( } } Some(Payload::Resize(resize)) => { - let _ = write_half - .window_change(resize.cols, resize.rows, 0, 0) - .await; + if tty { + let _ = write_half + .window_change(resize.cols, resize.rows, 0, 0) + .await; + } } Some(Payload::Start(_)) | None => {} } From 77622414bced30d95d4c7fdae3033223fac3da9a Mon Sep 17 00:00:00 2001 From: Artem Lytvyn Date: Thu, 16 Jul 2026 15:35:46 +0100 Subject: [PATCH 2/2] test(server): add unit test for tty=false in interactive exec validation Signed-off-by: Artem Lytvyn --- crates/openshell-server/src/grpc/sandbox.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crates/openshell-server/src/grpc/sandbox.rs b/crates/openshell-server/src/grpc/sandbox.rs index 6a3592bc54..770389c073 100644 --- a/crates/openshell-server/src/grpc/sandbox.rs +++ b/crates/openshell-server/src/grpc/sandbox.rs @@ -2626,6 +2626,21 @@ mod tests { assert!(err.message().contains("environment")); } + #[test] + fn interactive_exec_accepts_tty_false() { + use openshell_core::proto::exec_sandbox_input; + let msg = ExecSandboxInput { + payload: Some(exec_sandbox_input::Payload::Start(ExecSandboxRequest { + sandbox_id: "test-id".to_string(), + command: vec!["bash".to_string()], + tty: false, + ..Default::default() + })), + }; + let req = validate_interactive_exec_start(Some(msg)).unwrap(); + assert!(!req.tty); + } + #[test] fn interactive_exec_accepts_valid_start() { use openshell_core::proto::exec_sandbox_input;