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 fromcontent.mdfiles
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:stdioorhttp(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/
├── content.md # Root page (/)
├── docs/
│ ├── content.md # /docs
│ └── api/
│ └── content.md # /docs/api
└── about/
└── content.md # /about
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
-> /pathsyntax (e.g.,> Go to Docs -> /docs) - Empty lines are skipped
- Subdirectories automatically become child navigation nodes
- Directories without
content.mdget 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=[...]toPage().
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 import (
TermStreamServer, Page, Widget, WidgetType,
PageNode, create_page_node, create_server_from_nodes,
visualise, to_yaml, from_yaml, to_directory, from_directory
)
# Build a hierarchical page tree
home = PageNode("/home", "Home") \
.with_widget(Widget("h1", WidgetType.HEADING, (0, 0), "Welcome", bold=True))
docs = PageNode("/docs", "Docs") \
.with_widget(Widget("h1", WidgetType.HEADING, (0, 0), "Documentation", bold=True))
# Add children
home.add_child(docs)
# Convert to server
server = create_server_from_nodes(home)
# Visualize the route tree
print(visualise(home))
# Export to YAML
to_yaml(home, "config.yaml")
# Export to directory structure with content.md files
to_directory(home, "./my_app")
# Load from YAML or directory
root = from_yaml("config.yaml")
root = from_directory("./my_app")
Available functions:
PageNode(path, title)— Create a node at the given pathcreate_page_node(path, title)— Factory function for PageNodecreate_server_from_nodes(root)— Convert a PageNode tree to a TermStreamServervisualise(node)— Print the route tree structureto_yaml(node, path)— Export the tree to a YAML filefrom_yaml(path)— Load a PageNode tree from YAMLto_directory(node, dir_path)— Export the tree to a directory with content.md filesfrom_directory(dir_path)— Load a PageNode tree from a directory 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.mdfiles - 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
--sslor configuressl: truein YAML to enable encrypted connections. - Origin filtering: Use
--allowed-originsto restrict which origins can connect to the WebSocket server. - Dynamic app loading: The
--appflag executes arbitrary Python code. It can be disabled by settingTERMSTREAM_DISABLE_APP=1. Prefer--config(YAML) or--directoryfor untrusted content.
Disclosure
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 install termstream |
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
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file termstream_py-0.1.0.tar.gz.
File metadata
- Download URL: termstream_py-0.1.0.tar.gz
- Upload date:
- Size: 37.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ec9010ee9eba8a22220d7b98eb9ede1fd6969e950de9b791ea76d5bf90f0faf
|
|
| MD5 |
e4b1ca947320b9aa8fb135b5b927edf3
|
|
| BLAKE2b-256 |
dcae3b3be8f49b52c7e8f51a79e5280eefb8dce33501f586adf827d6aadd98c4
|
File details
Details for the file termstream_py-0.1.0-py3-none-any.whl.
File metadata
- Download URL: termstream_py-0.1.0-py3-none-any.whl
- Upload date:
- Size: 31.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ed3357c4fcc6629a5158ddbb0146d9c562ac3449e1a0b51a8af74d2ce5aa45d
|
|
| MD5 |
6716eab6a63c3d96014254190a1a0a3a
|
|
| BLAKE2b-256 |
b6ecb3613d425ca493f84bd9a8a78faee31f078d7d3e6c019c7d53545a756c70
|