8 Commits
Author SHA1 Message Date
valknar c7bc4421c5 Add a searchable combobox control for enum variables
select/radio don't scale to enums with dozens of choices. Reuses the
same cmdk Command/Popover primitives multi-select already uses, just
single-valued instead of an array.
2026-08-16 17:21:11 +02:00
valknarandClaude Sonnet 5 c6337ea942 Run prettier across the repo, exclude the lockfile from it
Prettier had never been run in --check mode here before, so this had
drifted across most files (markdown tables, long option() chains,
line wrapping). Purely formatting, no logic changes - needed so a CI
format:check gate can actually pass. Adds .prettierignore for
pnpm-lock.yaml specifically: prettier's YAML formatter rewrites every
quoted key (single -> double quotes) producing an ~8700-line diff of
pure noise on a file pnpm itself owns the formatting of.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 13:55:25 +02:00
valknarandClaude Sonnet 5 3f379ca2ac Flatten the repo: move everything out of app/ to the root
Now that the CLI and the Next.js app are one package, nesting it inside
app/ served no purpose - the repo root itself becomes the published
npm package. Merges app/.gitignore and app/README.md into the root
versions, drops the now-duplicate app/LICENSE, and updates path
references (README, docs/ARCHITECTURE.md, docs/CONFIG_REFERENCE.md,
package.json's repository.directory) that assumed the app/ nesting.

Also fixes a real bug this surfaced: the in-app docs viewer resolved
docs/ relative to process.cwd(), which only worked by accident when the
CLI happened to be invoked from app/'s parent directory. A first attempt
at fixing it with import.meta.dirname broke instead, for the same
cross-module-graph reason config-path resolution already documented -
Next compiles Route Handlers through a separate module graph that
doesn't preserve source-relative import.meta paths. Fixed by exposing
the app root via TRIGGERSHELL_APP_ROOT (set once in server.ts, where
import.meta *does* resolve correctly), the same pattern already used
for TRIGGERSHELL_CONFIG_PATH.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 11:14:01 +02:00
valknarandClaude Sonnet 5 30350d80f4 Replace the Python CLI with a Node CLI, add a systemd service command
The app is already 100% Node, so the Python launcher was pure overhead - it
existed mainly to bootstrap Node, which is circular. The CLI is now merged
into app/ (the single published npm package): `triggershell start` validates
the config and imports server.ts directly in-process, so server.ts's own
SIGTERM/SIGINT handling just works with no signal-relay/child-process layer
needed. `dev` is dropped from the public CLI (contributors use `pnpm --dir
app dev` directly); there's no `build` command either, since the package
ships a prebuilt `.next` via a `prepack` hook. Adds `triggershell service
install|uninstall|status` for running as a per-user or system systemd unit.

Also fixes two bugs found while wiring this up: server.ts resolved `.next`
relative to `process.cwd()`, which broke once the CLI could run from a
directory other than the app itself; and an explicitly-`files`-listed
package directory bypasses .npmignore for its subpaths, so `.next/cache`
was inflating the npm tarball to ~670MB (now stripped in `prepack`, ~7MB).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 11:03:25 +02:00
valknarandClaude Sonnet 5 acc6715a88 Allow timeoutSeconds: 0 to mean no timeout
Previously every script was forced to have a positive timeout capped
at 24h, with no way to run something genuinely unbounded. execa only
enforces its timeout option when it's greater than 0, so this just
relaxes the schema's lower bound from positive to >= 0 and lets that
flow through unchanged - no engine logic needed beyond a clarifying
comment. The 1800s default and 86400s cap for finite timeouts are
unchanged; 0 is an explicit opt-in, not the default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 20:57:24 +02:00
valknarandClaude Sonnet 5 ac0e645be3 Store password/token hashes in .env by default too
triggershell users add/add-token now generate a TRIGGERSHELL_USER_<name>_
PASSWORD_HASH / TRIGGERSHELL_TOKEN_<name>_HASH variable in .env (creating
or updating it idempotently) and print a ${VAR} snippet to paste into
auth.users/auth.tokens, instead of printing the raw hash. Pass --inline to
get the old behavior, since a hash - unlike sessionSecret - is safe to
store directly in the config (same trust model as /etc/shadow); this just
gives people who don't want it there at all an easy option.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 19:31:41 +02:00
valknarandClaude Sonnet 5 80c11d3bd3 Externalize auth secrets to .env and rename default config to triggershell.yml
sessionSecret was previously baked directly into the scaffolded config file;
`triggershell init` now generates a .env with TRIGGERSHELL_SESSION_SECRET
instead and references it via ${VAR} interpolation, keeping the actual
secret out of the (often committed) config file. `triggershell dev/start/
validate` load that .env automatically without overriding real env vars.

Also renames the default config filename from triggershell.config.yaml to
triggershell.yml throughout the CLI, app, docs, and examples.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 19:15:03 +02:00
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