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:
@@ -1,12 +1,18 @@
|
||||
import { Command } from "commander";
|
||||
import { doctorCommand } from "./commands/doctor";
|
||||
import { initCommand } from "./commands/init";
|
||||
import { runCommand } from "./commands/run";
|
||||
import { scriptsListCommand, scriptsShowCommand } from "./commands/scripts";
|
||||
import { serviceInstallCommand, serviceStatusCommand, serviceUninstallCommand } from "./commands/service";
|
||||
import { startCommand } from "./commands/start";
|
||||
import { usersAddCommand, usersAddTokenCommand } from "./commands/users";
|
||||
import { validateCommand } from "./commands/validate";
|
||||
import { getVersion } from "./version";
|
||||
|
||||
function collect(value: string, previous: string[]): string[] {
|
||||
return [...previous, value];
|
||||
}
|
||||
|
||||
const program = new Command("triggershell")
|
||||
.version(getVersion())
|
||||
.description("Launch the TriggerShell web app: run your configured shell scripts from a browser.");
|
||||
@@ -40,6 +46,45 @@ program
|
||||
.option("-c, --config <path>", "Path to the config file.")
|
||||
.action(doctorCommand);
|
||||
|
||||
const scripts = program.command("scripts").description("List and inspect configured scripts.");
|
||||
|
||||
scripts
|
||||
.command("list")
|
||||
.description("List configured scripts.")
|
||||
.option("-c, --config <path>", "Path to the config file.")
|
||||
.action(scriptsListCommand);
|
||||
|
||||
scripts
|
||||
.command("show <scriptId>")
|
||||
.description("Show a script's command and variables.")
|
||||
.option("-c, --config <path>", "Path to the config file.")
|
||||
.action(scriptsShowCommand);
|
||||
|
||||
program
|
||||
.command("run <scriptId>")
|
||||
.description("Run a configured script.")
|
||||
.option("-c, --config <path>", "Path to the config file.")
|
||||
.option(
|
||||
"--var <keyValue>",
|
||||
"Set a variable, e.g. --var environment=staging (repeatable; repeat the same name for a multiselect variable).",
|
||||
collect,
|
||||
[],
|
||||
)
|
||||
.option("--host <host>", "Override the host from the config file.")
|
||||
.option("--port <port>", "Override the port from the config file.", (v) => Number(v))
|
||||
.option(
|
||||
"--token <token>",
|
||||
"API token for an already-running server (or set TRIGGERSHELL_API_TOKEN).",
|
||||
)
|
||||
.option("--local", "Always run in this process, even if the web server is reachable.", false)
|
||||
.option(
|
||||
"--remote",
|
||||
"Require a reachable web server; don't fall back to running locally.",
|
||||
false,
|
||||
)
|
||||
.option("--no-wait", "Print the run ID and exit immediately instead of streaming output.")
|
||||
.action(runCommand);
|
||||
|
||||
const users = program.command("users").description("Manage auth users and API tokens defined in your config file.");
|
||||
|
||||
users
|
||||
|
||||
Reference in New Issue
Block a user