Skip to main content

Unified streaming terminal UI framework (client + server)

Project description

termstream-py

Unified streaming terminal UI framework (client + server)

TermStream is a Python-based framework for building streaming terminal UI applications with WebSocket communication, curses-based browser client, and MCP (Model Context Protocol) agent support.

Installation

pip install termstream-py

Prerequisites

  • Python >= 3.8

Usage

CLI Commands

After installation, the termstream command is available:

# Start the WebSocket server with a Python app module
termstream server -a path/to/your_app.py

# Start the WebSocket server with a YAML configuration file
termstream server -c path/to/your_config.yaml

# Start the WebSocket server from a directory tree (content.md files)
termstream server -d path/to/your_directory/

# Run the terminal browser client
termstream browser -u ws://localhost:8765

# Run MCP server for AI agents
termstream mcp -a path/to/your_app.py --transport stdio
termstream mcp -c path/to/your_config.yaml --transport stdio

CLI Options

server

  • -u, --url - Server WebSocket URL (default: ws://localhost:8765)
  • -a, --app - Path to app module (e.g., myapp.py)
  • -c, --config - Path to YAML configuration file
  • -d, --directory - Path to directory tree for building routes from content.md files

browser

  • -u, --url - Server WebSocket URL (default: ws://localhost:8765)

mcp

  • -a, --app - Path to app module (e.g., myapp.py)
  • -c, --config - Path to YAML configuration file
  • -p, --port - MCP HTTP port (default: 3000)
  • --transport - MCP transport type: stdio or http (default: stdio)

YAML Configuration

Define your server pages in a YAML file instead of code:

host: localhost
port: 8765

pages:
  - path: /home
    title: Home
    widgets:
      - type: heading
        pos: [0, 0]
        text: Welcome to TermStream
        style:
          bold: true
      - type: text
        pos: [2, 0]
        text: Streaming terminal UI framework.
      - type: button
        pos: [4, 0]
        text: "-> Docs"
        action:
          type: navigate
          target: /docs

Widget types: text, heading, button, input

Style options: bold, italic, underline

Action types: navigate (with target path)

Note: Widget id is optional. If omitted, a unique id is auto-generated. Provide an explicit id only for widgets that need server-side updates or input handling.

Directory-Based Configuration

Instead of a YAML file, you can define your application as a directory tree where each directory contains a content.md file:

my_app/
├── config.yaml         # Optional: host/port settings
├── content.md          # Root page (/)
├── docs/
│   ├── content.md      # /docs
│   └── api/
│       └── content.md  # /docs/api
└── about/
    └── content.md      # /about

config.yaml in Directory

Place an optional config.yaml at the root of your directory to set the host and port:

host: 0.0.0.0
port: 9000

Precedence for host/port (highest to lowest):

  1. config.yaml in the directory root (if the field is present)
  2. CLI arguments
  3. Built-in defaults (localhost, 8765)

Each field is resolved independently, so you can set host in config.yaml and override port on the CLI (or vice versa). Omitting both falls back to the defaults.

Each content.md file defines the page content using a simple format:

# Page Title
This is some text content on the page.
Another line of text.
> Button Label
$ Input Placeholder

Format rules:

  • First line starting with # becomes a bold HEADING widget (also used as page title)
  • Regular lines become TEXT widgets
  • Lines starting with > become BUTTON widgets
  • Lines starting with $ become INPUT widgets
  • Buttons can include a navigation target using -> /path syntax (e.g., > Go to Docs -> /docs)
  • Empty lines are skipped
  • Subdirectories automatically become child navigation nodes
  • Directories without content.md get a blank page with a warning

Auto-resolving navigation works identically with directory-based configuration — the route hierarchy is built from the directory structure.

Auto-Resolving Navigation

Navigation items are automatically resolved from the route tree — no need to define nav manually. The server inspects all registered routes and builds a hierarchical navigation structure:

/                  → shows: home, docs
/home              → shows: (children of /home, if any)
/docs              → shows: (end of node) — no children

This means you only need to define your page paths and widgets. Navigation is generated automatically based on the URL structure. Leaf pages (with no child routes) display an (end of node) indicator that does nothing when activated.

Example: 3-Level Hierarchy

pages:
  - path: /              # Root
  - path: /console       # Layer 1
  - path: /console/fe3   # Layer 2 (leaf)
  - path: /switch        # Layer 1
  - path: /switch/fe3h   # Layer 2 (leaf)

At /, the nav shows Console and Switch. At /console, the nav shows Fe3. At /console/fe3, the nav shows (end of node).

Python API

from termstream_py import TermStreamServer, Page, Widget, WidgetType

app = TermStreamServer(host="localhost", port=8765)

@app.route("/home")
def home():
    return Page(
        title="Home",
        widgets=[
            Widget("h1", WidgetType.HEADING, (0, 0), "Welcome", bold=True),
            Widget("p1", WidgetType.TEXT, (2, 0), "Streaming terminal UI."),
        ]
    )

Note: Navigation items are auto-resolved from the route tree, so you no longer need to pass nav=[...] to Page().

PageNode Builder API

The PageNode builder provides a hierarchical, tree-based API for constructing complex applications. Pages are organized as a tree of nodes where each node can have child sub-pages:

from termstream_py.builder import (
    PageNode, create_page_node, create_server_from_nodes
)

# Build a hierarchical page tree
root = PageNode("root", path="/", host="0.0.0.0", port=9000) \
    .add_heading("Welcome")

docs = PageNode("docs", path="/docs") \
    .add_heading("Documentation")

# Add children
root.add_child(docs)

# Convert to server (uses node's host/port)
server = root.to_server()

# Convert to server, reading host/port from config.yaml in a directory
server = root.to_server_from_directory("./my_app")

# Visualize the route tree
print(root.visualise())

# Export to YAML
root.to_yaml("config.yaml")

# Export to directory structure with content.md files
root.to_directory("./my_app")

# Write config.yaml with host/port to a directory
root.write_config_yaml("./my_app")

# Load from YAML or directory (host/port preserved from config.yaml)
root = PageNode.from_yaml("config.yaml")
root = PageNode.from_directory("./my_app")

Available methods:

  • PageNode(name, path, title, host, port) — Create a node
  • with_host(host) / with_port(port) — Set host/port for config export
  • to_server(host, port) — Convert to TermStreamServer
  • to_server_from_directory(directory, default_host, default_port) — Convert to server, reading host/port from config.yaml
  • to_yaml(path) / from_yaml(path) — Export/import YAML
  • to_directory(path) / from_directory(path) — Export/import directory structure
  • write_config_yaml(directory) — Write host/port to config.yaml
  • visualise() — Print the route tree structure

MCP Integration

from termstream_py import TermStreamServer, init_mcp_server

app = TermStreamServer(host="localhost", port=8765)

# Initialize MCP server for AI agent integration
mcp_server = init_mcp_server(app)
mcp_server.run(transport="stdio")

Browser Controls

Key Action
→ / ← Switch between Nav and Content panes
↑↓ Focus nav item or content widget
Enter Activate focused element
/ Start search mode (nav pane)
Space / PageDown Scroll down
b / PageUp Scroll up
1-9 Navigate to specific nav item
ESC / Backspace Go back
q Quit

Features

  • WebSocket Server: Stream UI frames to terminal clients
  • Curses Browser: Terminal-based browser with navigation support
  • Auto-Resolving Nav: Navigation generated automatically from route hierarchy
  • Directory-Based Config: Build routes from a directory tree with content.md files
  • config.yaml Support: Optional host/port configuration in directory root
  • MCP Integration: AI agent support via Model Context Protocol
  • YAML Configuration: Define pages and widgets in YAML files
  • Route-based Pages: Define pages using decorators
  • Interactive Widgets: Text, headings, buttons, and input fields

HTTPS / WSS Support

TermStream supports encrypted WebSocket connections (WSS) for secure communication.

Installation

HTTPS requires the cryptography package. Install it with the optional ssl extra:

pip install termstream-py[ssl]

CLI Usage

Auto-generate self-signed certificate (development):

termstream server -c config.yaml --ssl

Use your own certificate:

termstream server -c config.yaml --ssl-cert /path/to/cert.pem --ssl-key /path/to/key.pem

YAML Configuration

host: localhost
port: 8765
ssl: true
ssl_cert: /path/to/cert.pem   # optional; auto-generated if omitted
ssl_key: /path/to/key.pem     # optional; auto-generated if omitted

Browser Client

Connect to WSS servers by using the wss:// scheme:

termstream browser -u wss://localhost:8765

Note: Self-signed certificates are for development only. For production, use certificates from a trusted CA (e.g., Let's Encrypt).

Security Notes

  • HTTPS/WSS: Use --ssl or configure ssl: true in YAML to enable encrypted connections.
  • Origin filtering: Use --allowed-origins to restrict which origins can connect to the WebSocket server.
  • Dynamic app loading: The --app flag executes arbitrary Python code. It can be disabled by setting TERMSTREAM_DISABLE_APP=1. Prefer --config (YAML) or --directory for untrusted content.

Other Versions

TermStream is available in multiple languages. Choose the version that best fits your ecosystem:

Version Package Installation
Python termstream-py pip install termstream-py
Rust termstream cargo install termstream
Node.js termstream-npm npm install termstream-npm

Rust Version

The Rust version (termstream-rust) provides a native binary with high-performance WebSocket server and crossterm-based TUI client. Install via Cargo:

# Install from crates.io
cargo install termstream

# Or build from source
cd termstream-rust
cargo build --release

The Rust version supports the same CLI commands (server, browser, mcp) and YAML configuration format as the Python version.

Node.js Version

The Node.js version (termstream-npm) wraps the Rust binary via npm, providing a drop-in solution for JavaScript/TypeScript projects. Install via npm:

npm install termstream-npm

The npm version automatically compiles the Rust backend during installation and exposes the same CLI interface.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

termstream_py-0.1.3.tar.gz (39.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

termstream_py-0.1.3-py3-none-any.whl (33.5 kB view details)

Uploaded Python 3

File details

Details for the file termstream_py-0.1.3.tar.gz.

File metadata

  • Download URL: termstream_py-0.1.3.tar.gz
  • Upload date:
  • Size: 39.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for termstream_py-0.1.3.tar.gz
Algorithm Hash digest
SHA256 680b7d1290139f8fd241db0db0419a1570619cdf0e58687bcbf7df199cfdc349
MD5 8e89c15467ffc7372c2680f1639cce57
BLAKE2b-256 9d24dc0bb5ce7bab8ff7da304d76d221fecfde12480eabcacfed9f9069d2a994

See more details on using hashes here.

File details

Details for the file termstream_py-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: termstream_py-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 33.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for termstream_py-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 70b055230bb36bd2925822a82e12f38e50c56d95e3d891272c80746c2b46f969
MD5 7c4a4f35abf85c48b405bac3e4b39688
BLAKE2b-256 86adc2ed73d79dd8cd30b0e0fff2d1a5e1c7ef0a70c992190316737143b1a584

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page