valknarandClaude Sonnet 5 7daaa95ab9 Hide the Next.js dev-mode route indicator badge
Sets devIndicators: false in next.config.ts. Compile/runtime error
overlays are unaffected - only the floating route-info badge is hidden.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 20:45:46 +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.yml (+ .env for secrets) 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.yml 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.yml, override with -c/--config or TRIGGERSHELL_CONFIG_PATH). Secrets referenced via ${VAR} (like auth.sessionSecret) are meant to live in a .env file next to the config, not in the config itself — triggershell init generates both. Minimal example:

server:
  host: 127.0.0.1
  port: 4173

auth:
  enabled: true
  sessionSecret: "${TRIGGERSHELL_SESSION_SECRET}" # >= 32 chars
  users:
    - username: admin
      passwordHash: "${TRIGGERSHELL_USER_ADMIN_PASSWORD_HASH}" # 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.yml (with a matching .env.example), 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 + .env (--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> [-c CONFIG] [--inline] Hash a password, store it in .env, and print a ${VAR} snippet for auth.users (--inline prints the raw hash instead)
triggershell users add-token <name> [-c CONFIG] [--inline] Generate an API token, store its hash in .env, and print a ${VAR} snippet for auth.tokens (--inline prints the raw hash instead)

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.
  • auth.sessionSecret should live in .env, not the config file — triggershell init sets this up for you. passwordHash/tokenHash are one-way hashes (not the secrets themselves), so storing them directly in the config is reasonably safe — the same trust model as /etc/shadow or .htpasswd — but triggershell users add/add-token store them in .env via ${VAR} by default too, for cases where you don't want them readable by anyone with config access at all. Pass --inline to get the old behavior of printing the raw hash to paste into the config.

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%