CLI: rename heartbeat tmux helper and log file path
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
17
docs/tmux.md
Normal 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 isn’t 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.
|
||||
@@ -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,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user