valknarandClaude Sonnet 5 ced99a8e75 Initial implementation of TriggerShell
A Python CLI (typer) that bootstraps Node/pnpm and launches a Next.js 16 web
app for running configured shell scripts: YAML config validated by a shared
Zod schema, dynamic per-script forms mapped to shadcn controls, argv-safe
execa execution with live WebSocket streaming, SQLite/Drizzle run history,
and optional argon2 session + API token auth.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 18:37:30 +02:00
2026-08-15 18:37:30 +02:00

TriggerShell

A CLI that launches a self-hosted web app for running your shell scripts: define scripts and their parameters in a config file, get a generated form UI, run them asynchronously with live streaming output, and keep a searchable history — all behind optional login, with a full REST/WS API for automation.

Features

  • One config file defines the server, auth, and every script (command, working dir, timeout, and typed/validated variables with defaults and choices).
  • Generated forms — each variable renders as the right control (text, number, slider, select, radio, checkbox, switch, multi-select) based on its type, with client- and server-side validation from the same schema.
  • Safe execution — scripts run via argv arrays, never a shell string. User input can never break out into shell metacharacters.
  • Live monitoring — stdout/stderr and status stream over WebSocket to an in-browser terminal view; cancel a run from the UI or the API.
  • Run history — every run's variables (secrets redacted), resolved command, status, exit code, and full log are persisted in SQLite.
  • Optional auth — built-in username/password sessions and API tokens, toggleable per config.
  • REST + WebSocket API for scripting your script runner.

Quickstart

pip install triggershell         # or: pip install -e . from a checkout
triggershell init                # scaffold triggershell.config.yaml in the current directory
triggershell users add admin     # create a login (skip if you set auth.enabled: false)
triggershell dev                 # start in dev mode and open the browser

Edit triggershell.config.yaml to add your own scripts (see Configuration below), then run triggershell start for a production build.

Requirements

  • Python >= 3.9
  • Node.js >= 20 (checked by the CLI; not auto-installed)
  • pnpm (auto-provisioned via Corepack if missing and Corepack is available)

Configuration

TriggerShell is driven entirely by one YAML file (default: ./triggershell.config.yaml, override with -c/--config or TRIGGERSHELL_CONFIG_PATH). Minimal example:

server:
  host: 127.0.0.1
  port: 4173

auth:
  enabled: true
  sessionSecret: "${TRIGGERSHELL_SESSION_SECRET}" # >= 32 chars
  users:
    - username: admin
      passwordHash: "$argon2id$..." # from `triggershell users add`
  tokens: []

database:
  path: .triggershell/triggershell.db
logs:
  dir: .triggershell/logs
  retentionDays: 30

scripts:
  - id: deploy-service
    name: Deploy Service
    command: bash
    args: ["./scripts/deploy.sh"]
    timeoutSeconds: 600
    variables:
      - name: environment
        type: enum
        choices: [staging, production]
        default: staging
        required: true
        passAs: arg
        argName: --env
      - name: dryRun
        type: boolean
        default: false
        passAs: flag
        argName: --dry-run

A full, richly-commented example lives at examples/triggershell.config.yaml, and the complete field-by-field reference is in docs/CONFIG_REFERENCE.md.

Each variable's type (string / number / boolean / enum / multiselect) picks a sensible default UI control; set control explicitly to override it (e.g. slider for a number, radio for an enum). passAs (arg / flag / env / stdin) decides how the validated value reaches the script — always as a discrete argv element or env var, never interpolated into a shell string.

CLI Usage

Command Description
triggershell init [PATH] Scaffold a new config file (--port, --auth/--no-auth, --force)
triggershell validate [-c CONFIG] Validate a config file (fast Python pre-flight + full Node/Zod schema)
triggershell dev [-c CONFIG] [--port] [--host] [--no-browser] Run in development mode (hot reload)
triggershell start [-c CONFIG] [--port] [--host] [--no-browser] [--skip-build] Build (if stale) and run in production mode
triggershell doctor Print environment/config diagnostics
triggershell users add <username> Hash a password and print a config snippet for auth.users
triggershell users add-token <name> Generate an API token and print a config snippet for auth.tokens

Web App Guide

  • Scripts (/) — every configured script as a card; click through to its run form.
  • Run form (/scripts/:id) — a generated form for the script's variables, validated before submit.
  • Run detail (/runs/:id) — live streaming terminal output, status, exit code, and a Cancel button while the run is active.
  • History (/runs) — every past run, filterable by script/status via the API (?scriptId=, ?status=).

Authentication

Set auth.enabled: false for trusted/local-only use — the app then requires no login at all. When enabled, sessions are signed/encrypted cookies (no server-side session store), and API clients can instead send Authorization: Bearer <token> using a token from users add-token. Passwords are hashed with argon2id; only the hash ever lives in the config file.

API Reference

Full reference with request/response shapes and curl examples: docs/API.md.

Method Path Notes
GET /api/healthz Unauthenticated readiness probe
POST /api/auth/login {username, password} → sets session cookie
POST /api/auth/logout Clears the session
GET /api/auth/session Current auth state
GET /api/scripts List configured scripts
GET /api/scripts/:scriptId Full script schema (variables, UI hints)
POST /api/scripts/:scriptId/runs Start a run: {variables: {...}}
GET /api/runs History: ?scriptId=&status=&limit=&cursor=
GET /api/runs/:runId Single run's status/metadata
POST /api/runs/:runId/cancel Cancel an active run
GET /api/runs/:runId/logs Full or tailed log output (?tail=&download=)
WS /ws/runs Subscribe to a run's live output/status; send cancel

Development

pnpm --dir app install
pnpm --dir app dev            # or: triggershell dev, which wraps this
pnpm --dir app lint
pnpm --dir app typecheck
pnpm --dir app db:studio      # browse the SQLite DB

Repo layout:

triggershell/    Python CLI (launcher/orchestrator only)
app/             Next.js app - all server logic (API, auth, script execution) lives here
examples/        A runnable example config + scripts
docs/            Config/architecture/API reference docs

See docs/ARCHITECTURE.md for how the pieces fit together.

Security Notes

  • Scripts are always spawned with an argv array (execa), never a shell string — variable values can never inject additional shell commands. A script's own command/args may still use shell: true as an explicit, documented opt-in when the script genuinely needs pipes/globs; that reintroduces shell interpretation of passAs: arg values, so prefer passAs: env for anything user-controlled in that case.
  • secret: true variables are masked in the UI and redacted from persisted run records; only the live child process ever sees the real value.
  • The server binds to 127.0.0.1 by default — set server.host explicitly to expose it further.
  • Route Handlers check auth themselves (requireAuth()); proxy.ts is only a fast, optimistic redirect layer, not the security boundary.

License

MIT — see LICENSE.

S
Description
A CLI that launches a self-hosted web app for running your shell scripts.
Readme MIT
659 KiB
Languages
TypeScript 97.8%
CSS 1.9%
JavaScript 0.3%