Serve Python CLI applications in the browser using ttyd
Project description
Terminaide
A handy Python library for serving CLI applications in a browser. Terminaide allows developers to instantly web-enable terminal-based Python applications without packaging or distribution overhead, making it ideal for prototypes, demos, and applications with small user bases.
How It Works
Terminaide builds on three core technical elements:
-
ttyd Management: Automatically handles the installation and lifecycle of ttyd (terminal over WebSocket) binaries for the current platform. This eliminates the need for manual ttyd configuration.
-
Single-Port Proxying: Routes all HTTP and WebSocket traffic through a single port, simplifying deployments in containers and cloud environments while maintaining cross-origin security.
-
FastAPI Integration: Seamlessly integrates with FastAPI applications, allowing terminals to coexist with traditional web pages and REST endpoints via flexible route prioritization.
Installation
Install it from PyPI via your favorite package manager:
pip install terminaide
# or
poetry add terminaide
Terminaide automatically installs and manages its own ttyd binary within the package, with no reliance on system-installed versions:
- On Linux: Pre-built binaries are downloaded automatically
- On macOS: The binary is compiled from source (requires Xcode Command Line Tools)
This approach ensures a consistent experience across environments and simplifies both setup and cleanup.
Usage
Terminaide offers three primary approaches: Single Terminal mode for quickly serving individual functions or scripts, Multi Terminal mode for integrating multiple terminals into a FastAPI application, and Meta Server mode for serving a server that itself serves terminals. Start with Single mode for simplicity, then graduate to Multi mode when you need more flexibility, or use Meta mode when you want to run an entire server in a browser terminal.
Solo Mode
The Solo Server provides the fastest way to web-enable a Python function or script. It creates a standalone web server with a single terminal and handles all the configuration details for you. Choose between Function mode or Script mode based on your use case.
Script Server
The absolute simplest way to use Terminaide is to serve an existing Python script that you don't want to modify:
from terminaide import serve_script
if __name__ == "__main__":
serve_script("my_script.py")
Function Server
However if you have even a little flexibility, you can serve a Python function directly from a single entry point. Just pass any Python function to serve_function() and it's instantly web-accessible:
from terminaide import serve_function
def hello():
name = input("What's your name? ")
print(f"Hello, {name}!")
if __name__ == "__main__":
serve_function(hello) # That's it!
Configuration Options
Both Function and Script modes accept these configuration options:
# For serve_function(func, **kwargs) or serve_script(script_path, **kwargs)
kwargs = {
# Server options
"port": 8000, # Web server port
"title": None, # Terminal title (auto-generated if not specified)
"debug": True, # Enable debug mode
"reload": False, # Enable auto-reload on code changes
"trust_proxy_headers": True, # Trust X-Forwarded-Proto headers
"template_override": None, # Custom HTML template path
"preview_image": None, # Custom preview image for social media sharing
# Terminal appearance
"theme": {
"background": "black", # Background color
"foreground": "white", # Text color
"cursor": "white", # Cursor color
"cursor_accent": None, # Secondary cursor color
"selection": None, # Selection highlight color
"font_family": None, # Terminal font
"font_size": None # Font size in pixels
},
# TTYD process options
"ttyd_options": {
"writable": True, # Allow terminal input
"interface": "127.0.0.1", # Network interface to bind
"check_origin": True, # Enforce same-origin policy
"max_clients": 1, # Maximum simultaneous connections
"credential_required": False, # Enable authentication
"username": None, # Login username
"password": None, # Login password
"force_https": False # Force HTTPS mode
},
# Environment variable handling
"forward_env": True, # Forward all environment variables (default)
# Alternative 1: "forward_env": False, # Disable environment forwarding
# Alternative 2: "forward_env": ["AWS_PROFILE", "PATH", "DJANGO_SETTINGS"], # Specific variables
# Alternative 3: "forward_env": { # With selective overrides
# "AWS_PROFILE": "dev", # Set specific value
# "DEBUG": "1", # Set specific value
# "PATH": None # Use value from parent process
# }
}
Apps Server
The Apps Server extends the capabilities of the Solo Server to integrate multiple terminals into an existing FastAPI application. This approach gives you more control over routing, allows multiple terminals to coexist with regular web endpoints, and provides additional configuration options.
You can use both functions and scripts in your terminal routes:
from fastapi import FastAPI
from terminaide import serve_apps
import uvicorn
app = FastAPI()
# Custom routes defined first take precedence
@app.get("/")
async def root():
return {"message": "Welcome to my terminal app"}
# Define a function to serve in a terminal
def greeting():
name = input("What's your name? ")
print(f"Hello, {name}!")
favorite = input("What's your favorite programming language? ")
print(f"{favorite} is a great choice!")
serve_apps(
app,
terminal_routes={
# Script-based terminals
"/cli1": "script1.py",
"/cli2": ["script2.py", "--arg1", "value"],
"/cli3": {
"client_script": "script3.py",
"title": "Advanced CLI"
},
# Function-based terminals
"/hello": greeting,
"/admin": {
"function": greeting,
"title": "Admin Greeting Terminal",
"preview_image": "admin_preview.png"
}
}
)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Advanced Configuration
The Apps Server includes all the configuration options from the Solo Server, plus these additional options:
serve_apps(
# Required parameters
app, # FastAPI application
terminal_routes={ # Dictionary mapping paths to scripts or functions
# Script formats
"/path1": "script1.py",
"/path2": ["script2.py", "--arg1", "value"],
"/path3": {
"client_script": "script3.py",
"args": ["--mode", "advanced"],
"title": "Custom Title",
"port": 7682, # Specific port for this terminal
"preview_image": "path3_preview.png" # Per-route custom preview
},
# Function formats
"/func1": my_function,
"/func2": {
"function": my_function,
"title": "Function Terminal",
"port": 7683, # Specific port for this terminal
"preview_image": "function_preview.png" # Per-route custom preview
}
},
# Additional multi-mode specific options
mount_path="/", # Base path for terminal mounting
ttyd_port=7681, # Base port for ttyd processes
preview_image="default_preview.png", # Default preview image for all routes
# Plus all options from single mode
port=8000,
title=None,
debug=True,
# etc.
)
Meta Server
The Meta Server enables running a server that itself serves terminal instances within a browser terminal. This creates a "meta-server" where both the server process and the terminals it manages are accessible via web browsers, with proper directory context preservation.
from fastapi import FastAPI
from terminaide import serve_apps, meta_serve
def run_my_server():
"""This function starts a server with multiple terminals"""
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Welcome to my terminal app"}
serve_apps(
app,
terminal_routes={
"/cli": "my_script.py",
"/admin": admin_function
}
)
if __name__ == "__main__":
# Run the entire server in a browser terminal
meta_serve(run_my_server)
The Meta Server accepts the same configuration options as the Solo Server, plus:
meta_serve(
func, # Function that starts your server
app_dir="/path/to/app", # Application directory (auto-detected if not specified)
port=8000, # Web server port
title=None, # Terminal title (auto-generated: "{func_name} Server")
# ... plus all other Solo Server options
)
Termin-Arcade Demo
The demo/ directory contains a client and server that demonstrate serveral ready-to-use configurations:
make serve # Default mode with instructions
make serve function # Function mode - demo of serve_function()
make serve script # Script mode - demo of serve_script()
make serve apps # Apps mode - HTML page at root with multiple terminals
make serve container # Run in Docker container, requires Docker Desktop
Pre-Requisites
- Python 3.12+
- Linux or macOS
- macOS users need Xcode Command Line Tools (
xcode-select --install)
Limitations
Terminaide is designed to support rapid prototype deployments for small user bases. As a result:
- Not intended for high-traffic production environments
- Basic security features (though ttyd authentication is supported)
- Windows installation not yet supported
- Terminal capabilities limited to what ttyd provides
Function Reloading Considerations
When using functions with serve_apps(), note the following:
- Functions defined in your main application file will work seamlessly
- Functions imported from separate modules work best when using
reload=False(default) - When using
reload=True, imported functions might not reload properly due to Uvicorn's reloading mechanism - For best development experience with imported functions, consider using script mode instead
Project details
Release history Release notifications | RSS feed
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 terminaide-0.5.1.tar.gz.
File metadata
- Download URL: terminaide-0.5.1.tar.gz
- Upload date:
- Size: 321.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86a5bb96f46b4b3330335769f50a4a68831941572c438d2c2054bfaf568dcf64
|
|
| MD5 |
86fcecdcba7fa85951fd8c7b1231427e
|
|
| BLAKE2b-256 |
bfefa4981cf7a2198f73951ddb36104feb6afc1e8709d42b4e6b78ebf43f5713
|
File details
Details for the file terminaide-0.5.1-py3-none-any.whl.
File metadata
- Download URL: terminaide-0.5.1-py3-none-any.whl
- Upload date:
- Size: 329.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91b9929978971ac2ac8f7ac045d661eb024632206953dcdcf84559152794c1ca
|
|
| MD5 |
c7f1d54cd107cf6ba19a77135711e0b2
|
|
| BLAKE2b-256 |
6277bdf9ffcca53c16b8d8ddf8b8205cd75ca2a0c95b9d92d511eb171b66a908
|