5 Commits
Author SHA1 Message Date
valknarandClaude Sonnet 5 e5a1b7ce53 Rename package to scoped @valknar/triggershell
CI / Checks (push) Successful in 46s
CI / Publish to npm registry (push) Successful in 48s
Matches vpinball-wasm's move back to a scoped name: an unscoped
package on this Gitea npm registry can't be mapped via a normal
`.npmrc` `@scope:registry=` entry, since the registry doesn't proxy
npmjs.org. Scoping lets any consumer add one registry line instead of
pinning a tarball URL per dependency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012hQoM3jJT1Lx7CMTciMzvD
2026-08-23 01:06:23 +02:00
valknar 90d68f2510 Fix prettier formatting
CI / Publish to npm registry (push) Successful in 49s
CI / Checks (push) Successful in 47s
CI caught this on the v1.1.0 tag push - format:check wasn't run locally before committing.
2026-08-19 18:10:28 +02:00
valknar 1fa8c5ba66 Bump version to 1.1.0
CI / Checks (push) Failing after 45s
CI / Publish to npm registry (push) Skipped
2026-08-19 18:07:35 +02:00
valknar b291a119d5 Emit raw JSON from service logs via journalctl -o cat
Strips journalctl's own prefix so each line is pino's raw JSON payload, pipeable into jq for pretty-printing without pulling pino-pretty into runtime dependencies.
2026-08-19 18:06:06 +02:00
valknar e63129d156 Add triggershell service logs command
CI / Checks (push) Failing after 48s
CI / Publish to npm registry (push) Skipped
Thin wrapper around journalctl, consistent with the existing status/uninstall wrappers around systemctl.
2026-08-19 17:58:41 +02:00
5 changed files with 50 additions and 6 deletions
+2 -1
View File
@@ -24,7 +24,7 @@ API for automation.
## Quickstart ## Quickstart
```bash ```bash
npm install -g triggershell # or: npx triggershell <command> for one-off use npm install -g @valknar/triggershell # or: npx @valknar/triggershell <command> for one-off use
triggershell init # scaffold triggershell.yml (+ .env for secrets) in the current directory 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 users add admin # create a login (skip if you set auth.enabled: false)
triggershell start # start the app and open the browser triggershell start # start the app and open the browser
@@ -113,6 +113,7 @@ the script — always as a discrete argv element or env var, never interpolated
| `triggershell service install [--system]` | Install a systemd unit that runs `triggershell start` (per-user by default, Linux only) | | `triggershell service install [--system]` | Install a systemd unit that runs `triggershell start` (per-user by default, Linux only) |
| `triggershell service uninstall [--system]` | Stop, disable, and remove the systemd unit | | `triggershell service uninstall [--system]` | Stop, disable, and remove the systemd unit |
| `triggershell service status [--system]` | Show the systemd unit's status | | `triggershell service status [--system]` | Show the systemd unit's status |
| `triggershell service logs [-n LINES] [--no-follow] [--system]` | Tail the systemd unit's logs (wraps `journalctl -o cat`, so each line is raw JSON - pipe through `jq` for pretty-printing) |
### Running scripts from the CLI ### Running scripts from the CLI
+5 -3
View File
@@ -74,9 +74,11 @@ installs a per-user unit (`~/.config/systemd/user/triggershell.service`, no root
targets `/etc/systemd/system/` instead and prints the `sudo` commands to run if not already root. targets `/etc/systemd/system/` instead and prints the `sudo` commands to run if not already root.
`install` reloads the systemd daemon but does not enable/start the unit itself — that's a separate, `install` reloads the systemd daemon but does not enable/start the unit itself — that's a separate,
explicit `systemctl --user enable --now triggershell`, since it's the point where the service explicit `systemctl --user enable --now triggershell`, since it's the point where the service
actually starts listening and running scripts. `triggershell service status`/`uninstall` are thin actually starts listening and running scripts. `triggershell service status`/`uninstall`/`logs` are
wrappers around `systemctl`; log tailing is just `journalctl --user -u triggershell -f` — not thin wrappers around `systemctl`/`journalctl` respectively - no unit-file parsing or log storage of
reimplemented. our own, journald already does that. `logs` passes `-o cat` so each line is the raw pino JSON
payload rather than journalctl's own timestamp/hostname/unit prefix - pipeable straight into `jq`
for pretty-printing without pulling `pino-pretty` into the CLI's runtime dependencies.
## Cross-module-graph state ## Cross-module-graph state
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "triggershell", "name": "@valknar/triggershell",
"version": "1.0.4", "version": "1.2.0",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"repository": { "repository": {
+23
View File
@@ -126,3 +126,26 @@ export async function serviceStatusCommand(
}); });
process.exitCode = result.exitCode ?? 1; process.exitCode = result.exitCode ?? 1;
} }
export interface ServiceLogsOptions extends ServiceScopeOptions {
follow?: boolean;
lines?: number;
}
export async function serviceLogsCommand(
opts: ServiceLogsOptions,
): Promise<void> {
const scope = scopeOf(opts);
const args = scope === "user" ? ["--user"] : [];
// -o cat strips journalctl's own prefix (timestamp/hostname/unit) so each line is the raw pino
// JSON payload - pipeable straight into `jq` or similar without journalctl's wrapper in the way.
args.push("-u", SERVICE_NAME, "-o", "cat");
if (opts.follow !== false) args.push("-f");
if (opts.lines !== undefined) args.push("-n", String(opts.lines));
const result = await execa("journalctl", args, {
stdio: "inherit",
reject: false,
});
process.exitCode = result.exitCode ?? 1;
}
+18
View File
@@ -5,6 +5,7 @@ import { runCommand } from "./commands/run";
import { scriptsListCommand, scriptsShowCommand } from "./commands/scripts"; import { scriptsListCommand, scriptsShowCommand } from "./commands/scripts";
import { import {
serviceInstallCommand, serviceInstallCommand,
serviceLogsCommand,
serviceStatusCommand, serviceStatusCommand,
serviceUninstallCommand, serviceUninstallCommand,
} from "./commands/service"; } from "./commands/service";
@@ -180,6 +181,23 @@ service
) )
.action(serviceStatusCommand); .action(serviceStatusCommand);
service
.command("logs")
.description("Tail the systemd unit's logs (journalctl).")
.option("-n, --lines <n>", "Number of recent log lines to show.", (v) =>
Number(v),
)
.option(
"--no-follow",
"Print recent logs and exit instead of tailing continuously.",
)
.option(
"--system",
"Target the system-wide unit instead of the per-user one.",
false,
)
.action(serviceLogsCommand);
if (process.argv.length <= 2) { if (process.argv.length <= 2) {
program.outputHelp(); program.outputHelp();
process.exit(1); process.exit(1);