CLI: rename heartbeat tmux helper and log file path

This commit is contained in:
Peter Steinberger
2025-11-26 18:00:23 +01:00
parent deded848ee
commit 7e5b3958cc
7 changed files with 44 additions and 5 deletions

View File

@@ -5,7 +5,8 @@
### Changes
- Web relay now supports configurable command heartbeats (`inbound.reply.heartbeatMinutes`, default 30m) that ping Claude with a `HEARTBEAT_OK` sentinel; outbound messages are skipped when the token is returned, and normal/verbose logs record each heartbeat tick.
- New `warelay heartbeat` CLI triggers a one-off heartbeat (web provider, auto-detects logged-in session; optional `--to` override). Relay gains `--heartbeat-now` to fire an immediate heartbeat on startup.
- Added `warelay relay:heartbeat` (no tmux) and `warelay relay:tmux:heartbeat` helpers to start relay with an immediate startup heartbeat.
- Added `warelay relay:heartbeat` (no tmux) and `warelay relay:heartbeat:tmux` helpers to start relay with an immediate startup heartbeat.
- Relay now prints the active file log path and level on startup so you can tail logs without attaching.
- Heartbeat session handling now supports `inbound.reply.session.heartbeatIdleMinutes` and does not refresh `updatedAt` on skipped heartbeats, so sessions still expire on idle.
## 1.1.0 — 2025-11-26

View File

@@ -45,7 +45,7 @@ Install from npm (global): `npm install -g warelay` (Node 22+). Then choose **on
| `warelay status` | Show recent sent/received messages | `--limit <n>` `--lookback <min>` `--json` `--verbose` |
| `warelay heartbeat` | Trigger one heartbeat poll (web) | `--provider <auto\|web>` `--to <e164?>` `--verbose` |
| `warelay relay:heartbeat` | Run relay with an immediate heartbeat (no tmux) | `--provider <auto\|web>` `--verbose` |
| `warelay relay:tmux:heartbeat` | Start relay in tmux and fire a heartbeat on start (web) | _no flags_ |
| `warelay relay:heartbeat:tmux` | Start relay in tmux and fire a heartbeat on start (web) | _no flags_ |
| `warelay webhook` | Run inbound webhook (`ingress=tailscale` updates Twilio; `none` is local-only) | `--ingress tailscale\|none` `--port <port>` `--path <path>` `--reply <text>` `--verbose` `--yes` `--dry-run` |
| `warelay login` | Link personal WhatsApp Web via QR | `--verbose` |
@@ -125,7 +125,7 @@ Best practice: use a dedicated WhatsApp account (separate SIM/eSIM or business a
- When `heartbeatMinutes` is set (default 30 for `mode: "command"`), the relay periodically runs your command/Claude session with a heartbeat prompt.
- If Claude replies exactly `HEARTBEAT_OK`, the message is suppressed; otherwise the reply (or media) is forwarded. Suppressions are still logged so you know the heartbeat ran.
- Override session freshness for heartbeats with `session.heartbeatIdleMinutes` (defaults to `session.idleMinutes`). Heartbeat skips do **not** bump `updatedAt`, so sessions still expire normally.
- Trigger one manually with `warelay heartbeat` (web provider only, `--verbose` prints session info). Use `warelay relay:heartbeat` for a full relay run with an immediate heartbeat, or `--heartbeat-now` on `relay`/`relay:tmux:heartbeat`.
- Trigger one manually with `warelay heartbeat` (web provider only, `--verbose` prints session info). Use `warelay relay:heartbeat` for a full relay run with an immediate heartbeat, or `--heartbeat-now` on `relay`/`relay:heartbeat:tmux`.
### Logging (optional)
- File logs are written to `/tmp/warelay/warelay.log` by default. Levels: `silent | fatal | error | warn | info | debug | trace` (CLI `--verbose` forces `debug`). Web-provider inbound/outbound entries include message bodies and auto-reply text for easier auditing.

View File

@@ -38,4 +38,5 @@ Goal: add a simple heartbeat poll for command-based auto-replies (Claude-driven)
- Expose CLI triggers:
- `warelay heartbeat` (web provider, defaults to first `allowFrom`; optional `--to` override)
- `warelay relay:heartbeat` to run the relay loop with an immediate heartbeat (no tmux)
- Relay supports `--heartbeat-now` to fire once at startup (including `relay:tmux:heartbeat`).
- `warelay relay:heartbeat:tmux` to run the same in tmux (detached, attachable)
- Relay supports `--heartbeat-now` to fire once at startup (including the tmux helper).

17
docs/tmux.md Normal file
View File

@@ -0,0 +1,17 @@
# tmux helpers (relay backgrounding)
## Why we ship tmux helpers
- Run the relay detached so your shell can close, while keeping an interactive pane you can reattach to.
- Provide a consistent start/attach workflow without adding a daemon mode or external process manager.
- Keep the relay code itself tmux-agnostic; tmux is only a launcher concern.
## Commands
- `warelay relay:tmux` — restarts the `warelay-relay` session running `pnpm warelay relay --verbose`, then attaches (skips attach when stdout isnt a TTY).
- `warelay relay:tmux:attach` — attach to the existing session without restarting it.
- `warelay relay:heartbeat:tmux` — same as `relay:tmux` but adds `--heartbeat-now` so Claude is pinged immediately on startup.
All helpers use the fixed session name `warelay-relay`.
## Logs
- The relay always writes to the configured file logger (defaults to `/tmp/warelay/warelay.log`); on start it prints the active log path and level.
- tmux is just for interactive viewing; you can also tail the log file or use another supervisor if you prefer.

View File

@@ -116,4 +116,14 @@ describe("cli program", () => {
expect(runtime.exit).not.toHaveBeenCalled();
runtime.exit = originalExit;
});
it("runs relay heartbeat tmux helper", async () => {
const program = buildProgram();
await program.parseAsync(["relay:heartbeat:tmux"], { from: "user" });
const shouldAttach = Boolean(process.stdout.isTTY);
expect(spawnRelayTmux).toHaveBeenCalledWith(
"pnpm warelay relay --verbose --heartbeat-now",
shouldAttach,
);
});
});

View File

@@ -6,6 +6,7 @@ import { webhookCommand } from "../commands/webhook.js";
import { loadConfig } from "../config/config.js";
import { ensureTwilioEnv } from "../env.js";
import { danger, info, setVerbose, setYes } from "../globals.js";
import { getResolvedLoggerSettings } from "../logging.js";
import {
loginWeb,
logoutWeb,
@@ -273,6 +274,8 @@ Examples:
)
.action(async (opts) => {
setVerbose(Boolean(opts.verbose));
const { file: logFile, level: logLevel } = getResolvedLoggerSettings();
defaultRuntime.log(info(`logs: ${logFile} (level ${logLevel})`));
const providerPref = String(opts.provider ?? "auto");
if (!["auto", "web", "twilio"].includes(providerPref)) {
defaultRuntime.error("--provider must be auto, web, or twilio");
@@ -406,6 +409,8 @@ Examples:
.option("--verbose", "Verbose logging", false)
.action(async (opts) => {
setVerbose(Boolean(opts.verbose));
const { file: logFile, level: logLevel } = getResolvedLoggerSettings();
defaultRuntime.log(info(`logs: ${logFile} (level ${logLevel})`));
const providerPref = String(opts.provider ?? "auto");
if (!["auto", "web"].includes(providerPref)) {
defaultRuntime.error("--provider must be auto or web");
@@ -584,7 +589,7 @@ Examples:
});
program
.command("relay:tmux:heartbeat")
.command("relay:heartbeat:tmux")
.description(
"Run relay --verbose with an immediate heartbeat inside tmux (session warelay-relay), then attach",
)

View File

@@ -28,6 +28,7 @@ type ResolvedSettings = {
level: LevelWithSilent;
file: string;
};
export type LoggerResolvedSettings = ResolvedSettings;
let cachedLogger: Logger | null = null;
let cachedSettings: ResolvedSettings | null = null;
@@ -87,6 +88,10 @@ export function getChildLogger(
return getLogger().child(bindings ?? {}, opts);
}
export function getResolvedLoggerSettings(): LoggerResolvedSettings {
return resolveSettings();
}
// Test helpers
export function setLoggerOverride(settings: LoggerSettings | null) {
overrideSettings = settings;