Skip to main content

Termlit is a small Python tool that provides an SSH server interface. It makes it easy to expose interactive applications over SSH — for example, to host AI-powered conversational services that users can connect to with an SSH client.

Project description

termlit

Termlit is a small Python tool that provides an SSH server interface. It makes it easy to expose interactive applications over SSH — for example, to host AI-powered conversational services that users can connect to with an SSH client.

Features

  • SSH server with ready-to-use credentials (or anonymous mode)
  • Built-in Rich welcome panels and simple text helpers
  • Rich-powered spinners that lock user input while background work is running
  • Request/response helpers (termlit.post) powered by requests
  • Password-masked input via termlit.input(..., hidden=True)
  • Session-scoped stdout redirection so print() just works
  • Upload helpers (termlit.upload_file(s)) that copy generated files into an upload_files/ directory so you can retrieve them easily
  • Download helpers (termlit.download_cmd) that generate ready-to-run scp commands or temporary HTTP links for your end users

Installation

Two common installation methods are shown below:

  • From PyPI (recommended for end users):
python -m pip install termlit
  • From GitHub (editable / development install):
git clone https://github.com/stoday/termlit.git
cd termlit
python -m pip install -e .

The editable install makes it easy to modify code and test changes locally.

Quick start

  1. Install the package (editable mode during development is fine):

    pip install -e .
    

    or, if you prefer uv:

    curl -LsSf https://astral.sh/uv/install.sh | sh  # install uv (once)
    uv pip install -e .
    
  2. Create a script, e.g. app.py:

    import termlit
    
    termlit.welcome(
        title="Welcome~",
        subtitle="version 1.0.0",
        description="This is a note",
    )
    
    while True:
        prompt = termlit.input("User question: ")
        if prompt.lower() in {"quit", "exit"}:
            termlit.goodbye("Goodbye! See you next time")
            break
    
        with termlit.spinner("dots", "Processing your question..."):
            response = termlit.post(
                url="https://httpbin.org/post",
                json={"question": prompt},
                log=False,  # Suppress automatic POST summary
            )
    
        termlit.write("Answer: " + str(response.json()))
    

    termlit.spinner blocks input by default, so users cannot queue input while a background task is running. By default termlit.input ignores empty input; allow empty input with allow_empty=True. Use termlit.input("input password: ", hidden=True) when you need password input — the input will be masked with *.

  3. Serve it over SSH:

    termlit run app.py --host 0.0.0.0 --port 2222 --reload
    

    Add --auth none to allow passwordless login (the default is --auth ssh, which requires a password). Or run it locally without SSH:

    termlit run app.py --local
    
  4. Connect from any SSH client (default credentials admin/password123):

    ssh admin@127.0.0.1 -p 2222
    

Local mode

If you want to run a Termlit app without starting an SSH server, add --local:

termlit run app.py --local

Notes:

  • Runs the app on your local terminal using stdin/stdout (no remote access).
  • --host, --port, --auth, --user, and --allow-anonymous are ignored in local mode.
  • Useful for quick iteration or single-user usage on the same machine.

CLI flags

  • --user name=secret: add/override login credentials (repeatable).
  • --auth {ssh,none}: choose between password-protected (ssh) or passwordless (none) sessions.
  • --allow-anonymous: accept any username/password combo.
  • --local: run a script locally without starting an SSH server (use with run).
  • --reload: watch the target script and restart the SSH server whenever it changes (development helper).
  • --host / --port: where the SSH server listens.
  • --version: display the installed Termlit version and exit.

Streaming output

When you already have a generator/iterable that yields text chunks (for example, tailing logs or chunked API responses), call termlit.write(..., stream=True) to forward each chunk immediately without buffering:

def stream_logs():
    for line in follow_log_file():
        yield line

termlit.write(stream_logs(), stream=True)

Strings/bytes are still treated as a single chunk, so you can safely switch the flag on even when a function sometimes returns plain text and sometimes returns an iterator.

Programmatic usage

You can also embed Termlit inside a Python process:

import termlit

def app():
    termlit.welcome("Inline app")
    termlit.write("Hello there!")
    termlit.goodbye()

if __name__ == "__main__":
    termlit.run(app, host="127.0.0.1", port=2222)

To allow passwordless sessions, pass auth_mode="none" to termlit.run.

Uploading files

Use the upload_files/upload_file helpers when your script needs to drop artifacts (reports, logs, etc.) into a directory that you can fetch later:

import termlit

# Copy a single file to ./upload_files (or $TERMLIT_UPLOAD_DIR) with progress
termlit.upload_files("build/output/report.pdf", show_progress=True)

# Copy multiple files and grab the resulting server-side paths
uploaded = termlit.upload_file(
    ["app.log", r"C:\temp\screenshot.png"],
    show_progress=True,
)
termlit.write("Saved files to:")
for path in uploaded:
    termlit.write(f" - {path}")

# Provide ready-to-run scp commands for the client
cmd = termlit.download_cmd(
    "report.pdf",
    source_dir="upload_files",
)
termlit.write("Run command locally to download:")
termlit.write(cmd)

# Or host a temporary HTTP download link
http_links = termlit.download_cmd(
    "report.pdf",
    source_dir="upload_files",
    type="http",
)
termlit.write("Or open in browser:")
termlit.write(http_links)

All files are copied into upload_files/ relative to where termlit run was executed (override via the TERMLIT_UPLOAD_DIR environment variable or the destination_dir argument). Pass show_progress=True to stream simple percentage updates back to the SSH client while a file is being copied. Use replace=True when you want to overwrite same-named files instead of letting Termlit append _1, _2, ... suffixes (the default collision-avoidance behaviour). Use termlit.download_cmd(...) to generate the scp command your users should run locally, pass source_dir="upload_files" when you want to specify the hosting folder, or set type="http" to spin up a temporary http.server over the target folder. Set TERMLIT_DOWNLOAD_HOST, TERMLIT_DOWNLOAD_PORT, TERMLIT_DOWNLOAD_USER, or TERMLIT_HTTP_PORT when the defaults are insufficient.

Downloading files

Once your script calls termlit.upload_file(...), you have two convenient ways to guide end users through downloading the artifacts:

  1. scp command – Call termlit.download_cmd("report.pdf", source_dir="upload_files") to generate a string like scp -P 2222 admin@<host>:/abs/path/report.pdf ./. Give this command to your user to run locally. Adjust host/port/user via env vars TERMLIT_DOWNLOAD_HOST, TERMLIT_DOWNLOAD_PORT, TERMLIT_DOWNLOAD_USER, or override using host=, port=, username=, destination=.
  2. HTTP download – Pass type="http", for example termlit.download_cmd("report.pdf", source_dir="upload_files", type="http"). This starts a temporary http.server (default port 8765, override with TERMLIT_HTTP_PORT) and returns a URL like http://<host>:8765/report.pdf. The user can download via a browser; server access logging is suppressed by default to avoid interfering with the SSH interface.

The HTTP mode requires all target files to be in the same folder—collect them into upload_files/ before generating a link. Remind users to stop the temporary HTTP server (restart the app or run a custom command) after downloading for security.

Repository layout

  • termlit/session.py – public helper implementations.
  • termlit/runtime.py – SSH server + script runner.
  • termlit/cli.py – command line interface (termlit run).
  • scripts/ssh_server_plain.py, scripts/telnet_server.py – original demo servers (optional utilities; they call an external FastAPI backend that you must run yourself).
  • scripts/start_services.py – helper script that starts the Telnet/SSH demos and forwards the --fastapi-url you provide.

Happy terminal building!

Changelog

0.4.1 (2026-03-24)

  • Bumped project version to 0.4.1.
  • Synced the local lockfile package version to 0.4.1.

Developer Overview

  • termlit/__init__.py: Re-exports Termlit's public API (welcome, input, upload_file, etc.). It also loads version information and binds the thread-local session on import.
  • termlit/session.py: The session implementation that backs all public helpers (UI, HTTP helpers, upload/download) and maintains the internal _current_session state.
  • termlit/runtime.py: Starts and manages the SSH server and the script runner; provides a reload/watchdog flow via serve_script_with_reloader.
  • termlit/cli.py: The CLI entrypoint for termlit; parses arguments for termlit run, manages authentication options, and invokes the runtime.
  • example_app.py: An example app demonstrating how to build a Termlit flow, upload files, and interact with an HTTP API.
  • scripts/ssh_server_plain.py / scripts/telnet_server.py: Legacy demo servers that showcase an interactive shell and optional integration with an external FastAPI backend.
  • scripts/start_services.py: A convenience helper to start the Telnet/SSH demos and forward a common --fastapi-url to both services.
  • tests/: Unit and integration tests; please add tests covering new helpers or runtime behavior when introducing features.
  • upload_files/: The default location for server-side uploaded artifacts (override with TERMLIT_UPLOAD_DIR).

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

termlit-0.4.1.tar.gz (32.5 kB view details)

Uploaded Source

Built Distribution

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

termlit-0.4.1-py3-none-any.whl (28.0 kB view details)

Uploaded Python 3

File details

Details for the file termlit-0.4.1.tar.gz.

File metadata

  • Download URL: termlit-0.4.1.tar.gz
  • Upload date:
  • Size: 32.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.20

File hashes

Hashes for termlit-0.4.1.tar.gz
Algorithm Hash digest
SHA256 a9d264551b8fd7cd09bc8a1881ff083bbf5fcc7630a76cf0a9f25c75990c789b
MD5 5176ceab5b3698e56e9fabe2f19393af
BLAKE2b-256 f297c5ee833e4f1adfe2d56f6f6dcdb58cd85da6adfe8ea08bb4a2ed87c1cf31

See more details on using hashes here.

File details

Details for the file termlit-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: termlit-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 28.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.20

File hashes

Hashes for termlit-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 38a3df1ec48d7a6c2a00179d770b8f1f2029840a5993a8b66bab2b4d9e2b2af9
MD5 96568a591f1d25d2aa6a0235e161df9c
BLAKE2b-256 49db9a4cf917b0a27a0b85f81610dba1d847f5d95cb924444ee3851276cc75cf

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