mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
feat(cli): add hermes send to pipe script output to any messaging platform (#27188)
Introduces a thin CLI wrapper around the existing send_message_tool so
shell scripts, cron scripts, CI hooks, and monitoring daemons can reuse
the gateway's already-configured platform credentials without
reimplementing each platform's REST client.
hermes send --to telegram "deploy finished"
echo "RAM 92%" | hermes send --to telegram:-1001234567890
hermes send --to discord:#ops --file report.md
hermes send --to slack:#eng --subject "[CI]" --file build.log
hermes send --list # all targets
hermes send --list telegram # filter by platform
Supports all platforms the send_message tool already does (Telegram,
Discord, Slack, Signal, SMS, WhatsApp, Matrix, Feishu, DingTalk, WeCom,
Weixin, Email, etc.), including threaded targets and #channel-name
resolution via the channel directory.
hermes_cli/send_cmd.py delegates to tools.send_message_tool.send_message_tool,
which means there is zero new platform-specific code. The subcommand just:
1. Bridges ~/.hermes/.env and top-level ~/.hermes/config.yaml scalars into
os.environ (same bootstrap the gateway does at startup) — required so
TELEGRAM_HOME_CHANNEL and friends are visible to load_gateway_config().
2. Resolves the message body from positional arg, --file, or piped stdin.
3. Calls the shared tool and translates its JSON result to exit codes:
0 success, 1 delivery failure, 2 usage error.
No running gateway is required for bot-token platforms (Telegram, Discord,
Slack, Signal, SMS, WhatsApp) — the tool hits each platform's REST API
directly. Plugin platforms that rely on a live adapter connection still
need the gateway running; the error message is forwarded verbatim.
- New guide: website/docs/guides/pipe-script-output.md covering real-world
patterns (memory watchdogs, CI hooks, cron pipes, long-running task
completion pings) and the security/gateway notes.
- Cross-links added from automate-with-cron.md ("no LLM? use hermes send")
and developer-guide/gateway-internals.md (delivery-path section).
tests/hermes_cli/test_send_cmd.py (20 tests, all green):
- Happy paths: positional message, stdin, --file, --file -, --subject,
--json, --quiet.
- Error paths: missing --to, missing body, file not found, tool returns
error payload (exit 1), tool skipped-send result (exit 0).
- --list: human output, --json output, platform filter, unknown platform.
- Env loader: bridges config.yaml scalars into env, does not override
existing env vars, gracefully handles missing files.
- Registrar contract: register_send_subparser() returns a working parser.
Smoke-tested end-to-end against a live Telegram bot before commit.
This commit is contained in:
parent
33528b428d
commit
29b1bd0e20
6 changed files with 1091 additions and 3 deletions
|
|
@ -186,7 +186,7 @@ Outgoing deliveries (`gateway/delivery.py`) handle:
|
|||
|
||||
- **Direct reply** — send response back to the originating chat
|
||||
- **Home channel delivery** — route cron job outputs and background results to a configured home channel
|
||||
- **Explicit target delivery** — `send_message` tool specifying `telegram:-1001234567890`
|
||||
- **Explicit target delivery** — `send_message` tool specifying `telegram:-1001234567890`, or the [`hermes send` CLI](/docs/guides/pipe-script-output) wrapping the same tool for shell scripts
|
||||
- **Cross-platform delivery** — deliver to a different platform than the originating message
|
||||
|
||||
Cron job deliveries are NOT mirrored into gateway session history — they live in their own cron session only. This is a deliberate design choice to avoid message alternation violations.
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ For the full feature reference, see [Scheduled Tasks (Cron)](/docs/user-guide/fe
|
|||
Cron jobs run in fresh agent sessions with no memory of your current chat. Prompts must be **completely self-contained** — include everything the agent needs to know.
|
||||
:::
|
||||
|
||||
:::tip Don't need the LLM? Use no-agent mode.
|
||||
For recurring watchdogs where the script already produces the exact message you want to send (memory alerts, disk alerts, CI pings, heartbeats), skip the LLM entirely with [script-only cron jobs](/docs/guides/cron-script-only). Zero tokens, same scheduler. You can ask Hermes to set one up for you in chat — the `cronjob` tool knows when to pick `no_agent=True` and writes the script for you.
|
||||
:::tip Don't need the LLM? You have two zero-token options.
|
||||
- **Recurring watchdog** where the script already produces the exact message (memory alerts, disk alerts, heartbeats): use [script-only cron jobs](/docs/guides/cron-script-only). Same scheduler, no LLM. You can ask Hermes to set one up for you in chat — the `cronjob` tool knows when to pick `no_agent=True` and writes the script for you.
|
||||
- **One-shot from a script that's already running** (CI step, post-commit hook, deploy script, externally-scheduled monitor): use [`hermes send`](/docs/guides/pipe-script-output) to pipe stdout or a file straight to Telegram / Discord / Slack / etc. without setting up a cron entry.
|
||||
:::
|
||||
|
||||
---
|
||||
|
|
|
|||
249
website/docs/guides/pipe-script-output.md
Normal file
249
website/docs/guides/pipe-script-output.md
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
---
|
||||
sidebar_position: 12
|
||||
title: "Pipe Script Output to Messaging Platforms"
|
||||
description: "Send text from any shell script, cron job, CI hook, or monitoring daemon to Telegram, Discord, Slack, Signal, and other platforms using `hermes send`."
|
||||
---
|
||||
|
||||
# Pipe Script Output to Messaging Platforms
|
||||
|
||||
`hermes send` is a small, scriptable CLI that pushes a message to any
|
||||
messaging platform Hermes is already configured for. Think of it as a
|
||||
cross-platform `curl` for notifications — you don't need a running
|
||||
gateway, you don't need an LLM, and you don't need to re-paste bot tokens
|
||||
into each of your scripts.
|
||||
|
||||
Use it for:
|
||||
|
||||
- System monitoring (memory, disk, GPU temp, long-running job finished)
|
||||
- CI/CD notifications (deploy done, test failure)
|
||||
- Cron scripts that need to ping you with results
|
||||
- Quick one-shot messages from a terminal
|
||||
- Piping any tool's output anywhere (`make | hermes send --to slack:#builds`)
|
||||
|
||||
The command reuses the same credentials and platform adapters that `hermes
|
||||
gateway` already uses, so there's no second configuration surface to
|
||||
maintain.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Plain text to the home channel for a platform
|
||||
hermes send --to telegram "deploy finished"
|
||||
|
||||
# Pipe in stdout from anything
|
||||
echo "RAM 92%" | hermes send --to telegram:-1001234567890
|
||||
|
||||
# Send a file
|
||||
hermes send --to discord:#ops --file /tmp/report.md
|
||||
|
||||
# Attach a subject/header line
|
||||
hermes send --to slack:#eng --subject "[CI] build.log" --file build.log
|
||||
|
||||
# Thread target (Telegram topic, Discord thread)
|
||||
hermes send --to telegram:-1001234567890:17585 "threaded reply"
|
||||
|
||||
# List every configured target
|
||||
hermes send --list
|
||||
|
||||
# Filter by platform
|
||||
hermes send --list telegram
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Argument Reference
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-t, --to TARGET` | Destination. See [target formats](#target-formats). |
|
||||
| `message` (positional) | Message text. Omit to read from `--file` or stdin. |
|
||||
| `-f, --file PATH` | Read the body from a file. `--file -` forces stdin. |
|
||||
| `-s, --subject LINE` | Prepend a header/subject line before the body. |
|
||||
| `-l, --list` | List available targets. Optional positional platform filter. |
|
||||
| `-q, --quiet` | No stdout on success (exit code only — ideal for scripts). |
|
||||
| `--json` | Emit the raw JSON result of the send. |
|
||||
| `-h, --help` | Show the built-in help text. |
|
||||
|
||||
### Target Formats
|
||||
|
||||
| Format | Example | Meaning |
|
||||
|--------|---------|---------|
|
||||
| `platform` | `telegram` | Send to the platform's configured home channel |
|
||||
| `platform:chat_id` | `telegram:-1001234567890` | Specific numeric chat / group / user |
|
||||
| `platform:chat_id:thread_id` | `telegram:-1001234567890:17585` | Specific thread or Telegram forum topic |
|
||||
| `platform:#channel` | `discord:#ops` | Human-friendly channel name (resolved against the channel directory) |
|
||||
| `platform:+E164` | `signal:+15551234567` | Phone-addressed platforms: Signal, SMS, WhatsApp |
|
||||
|
||||
Any platform Hermes ships adapters for works as a target:
|
||||
`telegram`, `discord`, `slack`, `signal`, `sms`, `whatsapp`, `matrix`,
|
||||
`mattermost`, `feishu`, `dingtalk`, `wecom`, `weixin`, `email`, and
|
||||
others.
|
||||
|
||||
### Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| `0` | Send (or list) succeeded |
|
||||
| `1` | Delivery failed at the platform level (auth, permissions, network) |
|
||||
| `2` | Usage / argument / config error |
|
||||
|
||||
Exit codes follow the standard Unix convention so your scripts can
|
||||
branch on them the same way they would on `curl` or `grep`.
|
||||
|
||||
---
|
||||
|
||||
## Message Body Resolution
|
||||
|
||||
`hermes send` resolves the message body in this order:
|
||||
|
||||
1. **Positional argument** — `hermes send --to telegram "hi"`
|
||||
2. **`--file PATH`** — `hermes send --to telegram --file msg.txt`
|
||||
3. **Piped stdin** — `echo hi | hermes send --to telegram`
|
||||
|
||||
When stdin is a TTY (no pipe), Hermes does **not** wait for input — you'll
|
||||
get a clear usage error instead. This keeps scripts from hanging if they
|
||||
accidentally omit the body.
|
||||
|
||||
---
|
||||
|
||||
## Real-World Examples
|
||||
|
||||
### Monitoring: Memory / Disk Alerts
|
||||
|
||||
Replace ad-hoc `curl https://api.telegram.org/...` calls in your watchdogs
|
||||
with a single portable line:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
ram_pct=$(free | awk '/^Mem:/ {printf "%d", $3 * 100 / $2}')
|
||||
if [ "$ram_pct" -ge 85 ]; then
|
||||
hermes send --to telegram --subject "⚠ MEMORY WARNING" \
|
||||
"RAM ${ram_pct}% on $(hostname)"
|
||||
fi
|
||||
```
|
||||
|
||||
Because `hermes send` reuses your Hermes config, the same script works on
|
||||
any host where Hermes is installed — no need to export bot tokens into
|
||||
each machine's environment manually.
|
||||
|
||||
:::tip Don't alert the gateway about itself
|
||||
For watchdogs that might fire when the gateway itself is struggling (OOM
|
||||
alerts, disk-full alerts), keep using a minimal `curl` call instead of
|
||||
`hermes send`. If the Python interpreter can't load because the box is
|
||||
thrashing, you still want that alert to go out.
|
||||
:::
|
||||
|
||||
### CI / CD: Build and Test Results
|
||||
|
||||
```bash
|
||||
# In .github/workflows/deploy.yml or any CI script
|
||||
if ./scripts/deploy.sh; then
|
||||
hermes send --to slack:#deploys "✅ ${CI_COMMIT_SHA:0:7} deployed"
|
||||
else
|
||||
tail -n 100 deploy.log | hermes send \
|
||||
--to slack:#deploys --subject "❌ deploy failed"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### Cron: Daily Report
|
||||
|
||||
```bash
|
||||
# Crontab entry
|
||||
0 9 * * * /usr/local/bin/generate-metrics.sh \
|
||||
| /home/me/.hermes/bin/hermes send \
|
||||
--to telegram --subject "Daily metrics $(date +%Y-%m-%d)"
|
||||
```
|
||||
|
||||
### Long-Running Tasks: Ping When Done
|
||||
|
||||
```bash
|
||||
./train.py --epochs 200 && \
|
||||
hermes send --to telegram "training done" || \
|
||||
hermes send --to telegram "training failed (exit $?)"
|
||||
```
|
||||
|
||||
### Scripting with `--json` and `--quiet`
|
||||
|
||||
```bash
|
||||
# Hard-fail a script if delivery fails; don't clutter logs on success
|
||||
hermes send --to telegram --quiet "keepalive" || {
|
||||
echo "Telegram delivery failed" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Capture the message ID for later editing / threading
|
||||
msg_id=$(hermes send --to discord:#ops --json "build started" \
|
||||
| jq -r .message_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Does `hermes send` Need the Gateway Running?
|
||||
|
||||
**Usually no.** For any bot-token platform — Telegram, Discord, Slack,
|
||||
Signal, SMS, WhatsApp Cloud API, and most others — `hermes send` calls
|
||||
the platform's REST endpoint directly using credentials from
|
||||
`~/.hermes/.env` and `~/.hermes/config.yaml`. It's a standalone subprocess
|
||||
that exits as soon as the message is delivered.
|
||||
|
||||
A live gateway is only required for **plugin platforms** that rely on a
|
||||
persistent adapter connection (for example, a custom plugin that keeps
|
||||
a long-lived WebSocket open). In that case you'll get a clear error
|
||||
pointing at the gateway; start it with `hermes gateway start` and retry.
|
||||
|
||||
---
|
||||
|
||||
## Listing and Discovering Targets
|
||||
|
||||
Before sending to a specific channel, you can inspect what's available:
|
||||
|
||||
```bash
|
||||
# Every target across every configured platform
|
||||
hermes send --list
|
||||
|
||||
# Just Telegram targets
|
||||
hermes send --list telegram
|
||||
|
||||
# Machine-readable
|
||||
hermes send --list --json
|
||||
```
|
||||
|
||||
The listing is built from `~/.hermes/channel_directory.json`, which the
|
||||
gateway refreshes every few minutes while it's running. If you see
|
||||
"no channels discovered yet", start the gateway once (`hermes gateway
|
||||
start`) so it can populate the cache.
|
||||
|
||||
Human-friendly names (`discord:#ops`, `slack:#engineering`) are resolved
|
||||
against this cache at send time, so you don't need to memorize numeric
|
||||
IDs.
|
||||
|
||||
---
|
||||
|
||||
## Comparison with Other Approaches
|
||||
|
||||
| Approach | Multi-platform | Reuses Hermes creds | Needs gateway | Best for |
|
||||
|----------|----------------|---------------------|---------------|----------|
|
||||
| `hermes send` | ✅ | ✅ | No (bot-token) | Everything below |
|
||||
| Raw `curl` to each platform | Each scripted separately | Manual | No | Critical watchdogs |
|
||||
| `cron` job with `--deliver` | ✅ | ✅ | No | Scheduled agent tasks |
|
||||
| `send_message` agent tool | ✅ | ✅ | No | Inside an agent loop |
|
||||
|
||||
`hermes send` is intentionally the simplest possible surface. If you need
|
||||
an agent to decide what to say, use the `send_message` tool from within a
|
||||
chat or cron job. If you need a scheduled run with LLM-generated content,
|
||||
use `cronjob(action='create', prompt=...)` with `deliver='telegram:...'`.
|
||||
If you just need to pipe a raw string, reach for `hermes send`.
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [Automate Anything with Cron](/docs/guides/automate-with-cron) —
|
||||
scheduled jobs whose output auto-delivers to any platform.
|
||||
- [Gateway Internals](/docs/developer-guide/gateway-internals) —
|
||||
the delivery router that `hermes send` shares with cron delivery.
|
||||
- [Messaging Platform Setup](/docs/user-guide/messaging/) —
|
||||
one-time configuration for each platform.
|
||||
Loading…
Add table
Add a link
Reference in a new issue