feat: Implement real-time streaming output with interactive multi-node UI

Open
#68 0 comments 0 reactions 1 assignee View on GitHub

@inureyes is already working on this.

Since Oct 29, 2025.

  • #71 by @inureyes โ€” merged
  • #73 by @inureyes โ€” merged

Assessment

This issue has not been assessed yet.

Description

priority:medium status:backlog type:enhancement

๐Ÿ“‹ Overview

Implement real-time command output streaming capability for SSH execution, inspired by PR #37 but with enhanced multi-node UI support. Currently, bssh waits for commands to complete before showing any output, making it difficult to monitor long-running operations across multiple nodes.

Note: This issue has been significantly updated to reflect the current implementation status as of v1.4.0. Most planned features have been implemented.

โœ… Implementation Status Summary

Feature Status Location
Core Streaming API โœ… Complete src/ssh/tokio_client/channel_manager.rs
Independent Stream Management โœ… Complete src/executor/stream_manager.rs
Simple Output Modes (--stream, --output-dir) โœ… Complete src/cli.rs, src/executor/output_mode.rs
Interactive TUI (ratatui) โœ… Complete src/ui/tui/
Summary View โœ… Complete src/ui/tui/views/summary.rs
Detail View โœ… Complete src/ui/tui/views/detail.rs
Split View โœ… Complete src/ui/tui/views/split.rs
Diff View โœ… Complete src/ui/tui/views/diff.rs
Progress Parsing โœ… Complete src/ui/tui/progress.rs
TTY Detection โœ… Complete atty dependency
Memory Overflow Protection โœ… Complete RollingBuffer with 10MB limit
Comprehensive Tests ๐Ÿšง Partial Unit tests exist, more integration tests needed
Documentation ๐Ÿšง Partial Code documented, ARCHITECTURE.md needs update

๐ŸŽฏ Goals (Updated)

  1. Real-time Output Streaming: Enable streaming of stdout/stderr as commands execute โœ… Complete
  2. Multi-node Observability: Provide dynamic UI to monitor multiple nodes simultaneously โœ… Complete
  3. Independent Stream Management: Maintain separate output streams per node โœ… Complete
  4. Backward Compatibility: Maintain existing API and behavior โœ… Complete

๐Ÿ—๏ธ Architecture (Current Implementation)

Core Streaming Infrastructure โœ…
// Implemented in src/ssh/tokio_client/channel_manager.rs

/// Command output variants for streaming
#[derive(Debug, Clone)]
pub enum CommandOutput {
    StdOut(CryptoVec),
    StdErr(CryptoVec),
    ExitCode(u32),
}

impl Client {
    // Existing method (backward compatible) - uses streaming internally
    pub async fn execute(&self, cmd: &str) -> Result<CommandExecutedResult>;

    // Streaming method
    pub async fn execute_streaming(
        &self,
        command: &str,
        sender: Sender<CommandOutput>
    ) -> Result<u32, Error>;

    // Sudo password support
    pub async fn execute_with_sudo(
        &self,
        command: &str,
        sender: Sender<CommandOutput>,
        sudo_password: &SudoPassword,
    ) -> Result<u32, Error>;
}
Independent Stream Management โœ…
// Implemented in src/executor/stream_manager.rs

/// Independent output stream for a single node
pub struct NodeStream {
    pub node: Node,
    receiver: mpsc::Receiver<CommandOutput>,
    stdout_buffer: RollingBuffer,  // 10MB max with overflow protection
    stderr_buffer: RollingBuffer,
    status: ExecutionStatus,
    exit_code: Option<u32>,
    closed: bool,
}

/// Manager for coordinating multiple node streams
pub struct MultiNodeStreamManager {
    streams: Vec<NodeStream>,
}

/// Execution status for a node's command
pub enum ExecutionStatus {
    Pending,
    Running,
    Completed,
    Failed(String),
}
TUI Architecture โœ…
// Implemented in src/ui/tui/

pub mod app;           // TuiApp, ViewMode
pub mod event;         // Keyboard event handling
pub mod progress;      // Progress bar parsing
pub mod terminal_guard; // RAII terminal cleanup
pub mod views;         // summary, detail, split, diff

pub enum ViewMode {
    Summary,              // Show all nodes status
    Detail(usize),        // Focus on single node
    Split(Vec<usize>),    // Show multiple nodes in panes
    Diff(usize, usize),   // Compare two nodes side-by-side
}

๐Ÿ“ Multi-node UI (Implemented)

CLI Output Modes โœ…
# TUI Mode (default when TTY detected)
$ bssh -C production "apt-get update"
# โ†’ Opens interactive TUI with real-time monitoring

# Stream Mode (--stream flag)
$ bssh -C prod --stream "command"
[node1] Starting process...
[node2] Starting process...
[node1] Progress: 50%

# File Mode (--output-dir flag)
$ bssh -C prod --output-dir ./logs "command"
# Creates: ./logs/node1_TIMESTAMP.stdout, etc.

# Normal Mode (auto when piped)
$ bssh -C prod "uptime" | grep -v idle
TUI Views (All Implemented) โœ…
Summary View (Press Esc from other views)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Cluster: production - apt-get upgrade                    โ”‚
โ”‚ Total: 8 โ€ข โœ“ 3 โ€ข โœ— 1 โ€ข 4 in progress                    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ [1] node1                โœ“ Completed (exit: 0)          โ”‚
โ”‚ [2] node2                โŸณ [=========     ] 75%         โ”‚
โ”‚ [3] node3                โŸณ Running...                   โ”‚
โ”‚ [4] node4                โœ— Exit code: 1                 โ”‚
โ”‚ [5] node5                โŸณ [==           ] 25%          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ [1-9] Detail  [s] Split  [d] Diff  [q] Quit  [?] Help   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
Detail View (Press 1-9)

Full output view with scrolling and follow mode toggle (f key).

Split View (Press s)

Shows 2-4 nodes simultaneously in split panes.

Diff View (Press d)

Side-by-side comparison of two nodes.

Keyboard Controls โœ…
  • 1-9: Jump to node detail view
  • s: Enter split view
  • d: Enter diff view (select two nodes)
  • f: Toggle auto-scroll (follow mode)
  • โ†‘/โ†“: Scroll output
  • โ†/โ†’: Switch between nodes in detail view
  • Esc: Return to summary view
  • ?: Show help overlay
  • q: Quit

๐Ÿ“ฆ Dependencies (Current)

# Already implemented in Cargo.toml
ratatui = "0.29"      # TUI framework
crossterm = "0.29"    # Terminal control
atty = "0.2.14"       # TTY detection
tokio = { version = "1.47.1", features = ["full"] }
indicatif = "0.18"    # Progress indicators (parallel mode)

๐Ÿ› ๏ธ Implementation Status

Task 1: Core Streaming API โœ…
  • Implement execute_streaming() API
  • Add CommandOutput enum with StdOut/StdErr/ExitCode
  • Implement CommandOutputBuffer for internal use
  • Ensure execute() maintains backward compatibility (uses streaming internally)
  • Add error type for JoinError
  • Add execute_with_sudo() for sudo password handling
Task 2: Independent Stream Management โœ…
  • Implement NodeStream struct with independent buffering
  • Create MultiNodeStreamManager for coordinating streams
  • Implement non-blocking polling (poll() method)
  • Add per-node state management (ExecutionStatus)
  • Handle partial failures gracefully
  • Add RollingBuffer with 10MB limit for memory protection
Task 3: Simple Output Modes โœ…
  • Add --stream flag for interleaved output with [node] prefixes
  • Implement --output-dir for per-node file output
  • Add TTY detection (auto-enable TUI in terminals)
  • Update CLI argument parsing in src/cli.rs
  • Implement OutputMode enum (Normal/Stream/File/Tui)
Task 4: Interactive TUI โœ…
  • Add ratatui and crossterm dependencies
  • Implement summary view component
  • Add detail view with node switching
  • Implement split view mode (2-4 nodes)
  • Add diff mode for comparing two nodes
  • Implement progress parsing heuristics
  • Add keyboard navigation
  • Add auto-scroll control (follow mode)
  • Implement help overlay (? key)
  • Add terminal size validation with error message
Task 5: Testing & Documentation ๐Ÿšง
  • Unit tests for stream management
  • TUI integration tests with ratatui's test backend
  • Update ARCHITECTURE.md with TUI architecture
  • Add usage examples in README.md

๐ŸŽ Additional Features Implemented (Beyond Original Scope)

The following features were implemented but not originally planned in this issue:

  1. Sudo Password Support (execute_with_sudo())

    • Automatic sudo prompt detection
    • Secure password injection with PTY
    • Multiple sudo prompt handling (up to 10 per session)
    • Buffer size limits for security (64KB)
  2. Memory Protection

    • RollingBuffer with configurable max size (10MB default)
    • Automatic old data discard to prevent OOM
    • Overflow logging and warnings
  3. Terminal Guard

    • RAII-based terminal cleanup
    • Proper handling of crashes/panics
    • Cursor visibility management
  4. Progress Parsing Heuristics

    • Detection of XX% patterns
    • Status message extraction from output
    • Integration with summary view

๐ŸŽฏ Remaining Work

  1. Documentation

    • Update ARCHITECTURE.md with new TUI module structure
    • Add TUI screenshots to README
    • Document keyboard shortcuts in help output
  2. Testing

    • Add TUI snapshot tests using ratatui's test backend
    • Integration tests for streaming execution
    • Performance tests for large output handling
  3. Future Enhancements (Lower Priority)

    • Configurable buffer sizes via CLI/config
    • Output search/filtering within TUI
    • Session recording and playback
    • Per-node selective logging

๐Ÿ“ Current Project Structure (Relevant Files)

bssh/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ cli.rs                    # CLI with --stream, --output-dir flags
โ”‚   โ”œโ”€โ”€ executor/
โ”‚   โ”‚   โ”œโ”€โ”€ mod.rs                # ParallelExecutor exports
โ”‚   โ”‚   โ”œโ”€โ”€ output_mode.rs        # OutputMode enum
โ”‚   โ”‚   โ””โ”€โ”€ stream_manager.rs     # NodeStream, MultiNodeStreamManager
โ”‚   โ”œโ”€โ”€ ssh/
โ”‚   โ”‚   โ”œโ”€โ”€ client/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ mod.rs            # SshClient exports
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ command.rs        # execute(), execute_streaming()
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ”‚   โ””โ”€โ”€ tokio_client/
โ”‚   โ”‚       โ”œโ”€โ”€ mod.rs            # Client, CommandOutput exports
โ”‚   โ”‚       โ”œโ”€โ”€ channel_manager.rs # CommandOutput, execute_streaming()
โ”‚   โ”‚       โ””โ”€โ”€ ...
โ”‚   โ”œโ”€โ”€ ui/
โ”‚   โ”‚   โ”œโ”€โ”€ mod.rs                # UI exports
โ”‚   โ”‚   โ””โ”€โ”€ tui/
โ”‚   โ”‚       โ”œโ”€โ”€ mod.rs            # run_tui(), TuiExitReason
โ”‚   โ”‚       โ”œโ”€โ”€ app.rs            # TuiApp, ViewMode
โ”‚   โ”‚       โ”œโ”€โ”€ event.rs          # Keyboard handling
โ”‚   โ”‚       โ”œโ”€โ”€ progress.rs       # Progress parsing
โ”‚   โ”‚       โ”œโ”€โ”€ terminal_guard.rs # RAII terminal cleanup
โ”‚   โ”‚       โ””โ”€โ”€ views/
โ”‚   โ”‚           โ”œโ”€โ”€ mod.rs
โ”‚   โ”‚           โ”œโ”€โ”€ summary.rs
โ”‚   โ”‚           โ”œโ”€โ”€ detail.rs
โ”‚   โ”‚           โ”œโ”€โ”€ split.rs
โ”‚   โ”‚           โ””โ”€โ”€ diff.rs
โ”‚   โ””โ”€โ”€ commands/
โ”‚       โ””โ”€โ”€ exec.rs               # Execute command with output modes
โ””โ”€โ”€ Cargo.toml                    # ratatui, crossterm, atty dependencies

๐Ÿ“š References

โœ… Acceptance Criteria (Status)

  • execute_streaming() API works with single node
  • Multi-node execution maintains independent streams per node
  • Node switching is instant (no re-fetching of output)
  • Each node preserves scroll position and state when switching
  • --stream mode works in terminals and pipes
  • TUI activates automatically in interactive terminals
  • All existing tests pass (backward compatibility)
  • New tests cover streaming scenarios and view modes (partial)
  • Documentation updated (README, ARCHITECTURE.md) (partial)
Dominant language
Rust
Stars
65
Forks
7
Avg merge
1h 30m
Merged PRs (30d)
25

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up โ€” it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from lablup/bssh

All issues in lablup/bssh

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.