Add triggershell run/scripts - execute configured scripts from the CLI
`run <scriptId>` auto-detects whether the web server is already reachable (a quick /api/healthz check): - If it is, the run goes through the existing POST /api/scripts/:id/runs endpoint (token-authenticated, same as any other API client) and the CLI subscribes over /ws/runs exactly like a browser tab - so the run shows up live in Run History and any open browser watching it, with zero server-side changes, since the broadcast path has no idea a run was triggered by a click vs a CLI invocation. - If nothing's reachable, it calls startRun() directly in its own process (after its own migrateOnBoot/reconcileOrphanedRuns, so a from-scratch .triggershell/ works standalone) and streams output by listening on the same in-process runEvents emitter a WS client would otherwise be fed from - read-log-then-listen, the same ordering ws/server.ts's subscribe() already uses, so a fast script finishing before the listener attaches still gets its output printed. Both modes support --var name=value (repeatable; repeat a name for multiselect), --no-wait, and Ctrl-C cancellation through the same mechanism the web UI's Cancel button uses (a WS cancel message remotely, cancelRun() directly locally). `scripts list`/`scripts show` are local-only, no network - same direct-config-read pattern as `validate`/`doctor`. Extracts defaultValuesForScript() out of dynamic-form.tsx into src/lib/config/defaults.ts so the CLI's --var handling and the web form fill in a script's configured defaults identically instead of duplicating that logic. Verified live end-to-end: a CLI-triggered remote run was observed streaming to both the triggering CLI process and an independent WS client (simulating a browser tab) simultaneously; local-mode Ctrl-C confirmed to actually kill the spawned child process, not just the CLI; token, wrong-token, and TRIGGERSHELL_API_TOKEN auth paths all verified against a running auth-enabled server. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,10 @@ When `auth.enabled: true`, every endpoint below except `/api/healthz`, `/api/aut
|
||||
`/api/auth/session` requires either a valid session cookie or an `Authorization: Bearer <token>`
|
||||
header using a token from `triggershell users add-token`.
|
||||
|
||||
`triggershell run <scriptId>` is a first-party client of this exact REST + WS contract (see
|
||||
[Running scripts from the CLI](../README.md#running-scripts-from-the-cli)) - nothing below is
|
||||
CLI-specific.
|
||||
|
||||
## Auth
|
||||
|
||||
### `POST /api/auth/login`
|
||||
|
||||
@@ -40,6 +40,30 @@ there's no parent process relaying signals to a child, no separate lifecycle to
|
||||
(`tsx/esm/api`'s `register()`), so both the CLI's own `.ts` command files and `server.ts` run
|
||||
straight from source, with no compile/bundle step for either.
|
||||
|
||||
## `triggershell run` - a second, independent client of the same run pipeline
|
||||
|
||||
`triggershell run <scriptId>` (`src/cli/commands/run.ts`) never duplicates the spawn/streaming
|
||||
logic in `src/lib/runner/engine.ts` - it just calls it from a different position:
|
||||
|
||||
- If a server is reachable (`GET /api/healthz`), `run` is a plain HTTP+WS client: `POST
|
||||
/api/scripts/:id/runs` (the same route the web UI's "Run" button calls) starts the run *inside
|
||||
that server's process*, and `run` then subscribes over `/ws/runs` exactly like a browser tab
|
||||
would, using the same `ClientMessage`/`ServerMessage` protocol (`src/lib/ws/protocol.ts`). This
|
||||
is why a run started this way appears live in any open browser tab for free - the broadcast path
|
||||
(`emitRunMessage` → the `runEvents` listener in `src/lib/ws/server.ts` → every subscribed
|
||||
WebSocket) has no idea the run was triggered by a CLI instead of a click.
|
||||
- If nothing's reachable, `run` calls `startRun()` directly, in its own short-lived process (after
|
||||
its own `migrateOnBoot()`/`reconcileOrphanedRuns()`, so a from-scratch `.triggershell/` works
|
||||
standalone). Since it's in the same process as the run it just started, it doesn't need WS at
|
||||
all - it listens on the same in-process `runEvents` emitter a WS client would otherwise be fed
|
||||
from, using the exact "read the log file and current status first, then attach a live listener"
|
||||
ordering `subscribe()` in `ws/server.ts` already uses, for the same reason: a fast script can
|
||||
finish before a listener is attached.
|
||||
|
||||
Either way, `Ctrl-C` cancels the run through the existing mechanism for that mode - a `{"type":
|
||||
"cancel"}` WS message for the remote case, `cancelRun()` (`src/lib/runner/registry.ts`) directly
|
||||
for the local case - not a new cancellation path.
|
||||
|
||||
## Running as a systemd service
|
||||
|
||||
`triggershell service install` renders a unit file (`src/cli/lib/systemd.ts`) whose `ExecStart`
|
||||
|
||||
Reference in New Issue
Block a user