AgentStack
SKILL verified MIT Self-run

Hermes Remote

skill-osouthgate-agent-plus-skills-hermes-remote · by osouthgate

Manage a remote Hermes Agent deployment from the CLI — list/create/pause/trigger cron jobs, check status, inspect env vars, swap models, and send one-shot chat calls to the OpenAI-compatible /v1/chat/completions endpoint. Use this skill whenever you need to control a Hermes instance that lives on another machine (VPS, Coolify app, etc.), since the upstream `hermes` CLI only talks to a local gatew…

No reviews yet
0 installs
14 views
0.0% view→install

Install

$ agentstack add skill-osouthgate-agent-plus-skills-hermes-remote

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Hermes Remote? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

hermes-remote

Remote CLI for talking to a Hermes Agent deployment over its REST API. Stdlib-only Python 3. No pip installs.

The binary lives at ${CLAUDE_SKILL_DIR}/../../bin/hermes-remote inside this plugin, and bin/ is auto-added to PATH when the plugin is enabled — so call it as hermes-remote.

When to reach for this

  • The user asks about the state of a remote Hermes instance (status, model, crons, env).
  • The user wants to pause, resume, trigger, or delete a Hermes cron job.
  • The user wants to create a new cron — always use the skillify pattern below, not a prompt-only job.
  • The user wants to inspect which env vars Hermes can see vs has missing.

For one-shot chat calls (/v1/chat/completions), use hermes-remote chat "" — needs HERMES_CHAT_API_KEY set (distinct from the admin password).

hermes-remote chat "status?"                               # quick one-shot
hermes-remote chat --model anthropic/claude-haiku-4-5 ...  # override per-call
hermes-remote chat --max-tokens 200 "..."                  # cap output
hermes-remote chat --system "Be terse." "what is k8s?"     # add system prompt
hermes-remote chat --json "..."                            # full response body

For multi-turn conversations or streaming, use an OpenAI-compatible SDK pointed at $HERMES_URL/v1 — this wrapper is for one-shot calls and cron-friendly scripting.

When NOT to use this — fall back to the Hermes REST API directly

This wrapper covers a deliberately narrow slice: status, model, env list (read-only), config get/set, cron CRUD, and one-shot chat. It does NOT wrap a local CLI — there is no hermes remote-control CLI to fall back to. "Fall back" here means hit the Hermes REST API directly with curl (or http.client / requests) using the same cookie + bearer token flow hermes-remote uses internally. There is no other route.

Specific cases where you should talk to the Hermes endpoint directly, not through hermes-remote:

  • Streaming chat responses. hermes-remote chat buffers the full response then prints. For live token streaming, POST to $HERMES_URL/v1/chat/completions with "stream": true and an SSE-capable client.
  • Multi-turn conversations / conversation history. The chat subcommand is one-shot (system + user only). Maintain a messages[] array yourself and POST to /v1/chat/completions directly, or use an OpenAI-compatible SDK pointed at $HERMES_URL/v1.
  • File uploads, attachments, or anything multipart. Not wrapped. Hit the relevant Hermes endpoint with curl -F or a multipart-capable HTTP client.
  • Mutating env vars, editing existing cron jobs, managing deliveries/platforms/skills, or triggering a gateway restart. hermes-remote env list is read-only; cron create exists but there is no cron edit; gateway/platform/skill/delivery management isn't wrapped at all. Use curl against /api/env, /api/cron/jobs/ (PUT/PATCH), /api/gateway/*, /api/platforms/*, /api/skills/* with the session cookie + bearer token.
  • Authentication/login flows or session management. hermes-remote logs in silently on every call using HERMES_PASSWORD. If you need to inspect the raw /login response, manage cookies manually, or debug auth, hit /login directly.
  • Any Hermes endpoint the wrapper simply doesn't expose yet — job run history, raw logs, delivery channels, webhooks. Grab the cookie + token the same way (POST /login, scrape __HERMES_SESSION_TOKEN__ from /) and call the endpoint directly.

Don't get stuck in a loop. If the user needs something the wrapper clearly doesn't cover, switch to curl against the Hermes REST API immediately rather than contorting hermes-remote flags. The wrapper's job is to make the common read/cron paths ergonomic — not to be the only way in.

Configure

Config is layered. Highest precedence first:

  1. --env-file passed on the CLI
  2. .env.local / .env found by walking up from the current working directory
  3. Shell environment (including vars exported via Claude Code settings)

Project .env files override the shell — opposite of the usual rule. This lets you drop a .env in whichever repo you're working in and have it win over any globals, without having to unset anything.

Only HERMES_* and COOLIFY_* keys are picked up from .env files.

If no URL or password is configured, the CLI prints a suggestion showing exactly where to put them (project .env preferred, or ~/.claude/settings.json for a global default).

Set one URL mode and one password source.

URL — one of:

export HERMES_URL="https://hermes.example.com"
# OR for reverse-proxy-by-Host setups with flaky DNS:
export HERMES_VPS_IP="1.2.3.4"
export HERMES_HOST="hermes.example.com"

The second form hits the IP on port 80 directly and passes the Host header, letting Traefik/Caddy/Nginx route. Skips DNS.

Password — one of:

export HERMES_PASSWORD="..."                      # plain value
export HERMES_PASSWORD_CMD="pass show hermes"     # shell command, stdout is the password

If the Hermes instance is managed by Coolify, the script can fetch the password automatically:

export COOLIFY_URL="http://your-vps:8000"
export COOLIFY_API_KEY="..."
export HERMES_APP_UUID="..."

Admin user (optional, default admin@example.com):

export HERMES_ADMIN_USER="you@example.com"

Commands

hermes-remote status                    # gateway state + connected platforms
hermes-remote model                     # current LLM + context window
hermes-remote env list [--all]          # env vars Hermes knows about
hermes-remote config get [key]          # read full config, or a dotted-path key
hermes-remote config set    # mutate config.yaml via /api/config
hermes-remote cron list                 # all cron jobs, one line each
hermes-remote cron show     # full JSON for one job
hermes-remote cron pause 
hermes-remote cron resume 
hermes-remote cron trigger          # fire on next scheduler tick
hermes-remote cron remove  -y
hermes-remote cron create \
    --name  --schedule "every 15m" \
    --script /path/to/work.sh \
    --prompt "If script output contains ERROR, report it. Otherwise [SILENT]." \
    --deliver origin --model anthropic/claude-haiku-4-5

Most subcommands take --json for machine-parseable output. Recommended: pipe through jq for field extraction and filtering — e.g. hermes-remote cron list --json | jq '.[] | select(.state=="paused") | .id', hermes-remote config get --json | jq -r .model, hermes-remote status --json | jq .gateway_state. Every JSON payload is prefixed with a tool: {name, version} field so you can self-diagnose version drift (hermes-remote --version also works).

Changing the LLM model (READ THIS)

Hermes's config.yaml overrides env vars for keys it knows about. The LLM_MODEL env var only sets the default at first boot. Once config.yaml has a model, LLM_MODEL is ignored — so setting it in Coolify / Docker env and redeploying won't change anything.

To actually change the model, write it via the config API:

hermes-remote config set model anthropic/claude-haiku-4-5

No redeploy needed — it's live. Verify:

hermes-remote model   # reads /api/model/info; should match immediately

Same applies to any other config.yaml key (agent.maxturns, compression.threshold, telegram.channelprompts, etc.) — config set is the mutation path.

Skillify cron pattern (READ BEFORE CREATING A CRON)

A Hermes cron job has two input fields: prompt and script.

  • prompt runs inside a full agent LLM session on every tick. Default model, full context, tool loop. Expensive.
  • script runs before the agent starts — pure shell/python, zero LLM tokens. Its stdout becomes context for the agent.

If your cron does deterministic work (sync, embed, poll, sanity check), the work belongs in --script. The agent should only be invoked to decide "report or stay quiet" based on the script's output.

Template for any recurring deterministic cron:

hermes-remote cron create \
    --name  \
    --schedule "every 15m" \
    --script /path/on/hermes/host/to/worker.sh \
    --prompt "If the script output contains ERROR, report the relevant lines. Otherwise respond with only [SILENT]." \
    --model anthropic/claude-haiku-4-5 \
    --deliver origin

When the agent's response starts with [SILENT], Hermes suppresses delivery — so successful quiet runs cost essentially nothing.

Why this matters (real incident): A gbrain sync cron on our Hermes deployment was created with --prompt only, using the default Sonnet 4.6 model, running every 15 minutes. Each run invoked the full reasoning loop to execute two shell commands masquerading as reasoning work — burning OpenRouter credit fast. Fixing it to --script + minimal Haiku prompt was a roughly three-orders-of-magnitude cost reduction.

Rules of thumb for cron models:

  • Cheap recurring jobs → anthropic/claude-haiku-4-5 (fast, cheap, fine for "is this output an error?")
  • Genuinely latent work (entity extraction, summarisation, non-trivial judgment) → stay on Sonnet but cap max_tokens in Hermes config.

Quirks

  • is_build_time is not a valid field on Hermes env POSTs (will 422). Stick to {"key":..., "value":..., "is_preview": false, "is_literal": true}.
  • urllib.request overrides Host based on URL netloc; this script uses http.client with skip_host=True to honour explicit Host headers.
  • Hermes's /api/env only lists vars that match its config schema. A var can be in the container's shell env but not in this list — they're different surfaces.

Troubleshooting

  • HTTP 401 from /login: password wrong, or you're hitting the :3000 port directly and it has a separate basic-auth gate in front. Route through the main reverse-proxy URL.
  • Couldn't extract __HERMES_SESSION_TOKEN__: login worked but the SPA HTML didn't include the token. Probably a version mismatch; upstream Hermes < 0.10 may not expose it in the same place.
  • Unauthorized on specific /api/* endpoints with cookie + token set: that endpoint isn't wired up in your Hermes version, or you're on a preview that renamed it. Try listing with --json on a known-good endpoint first to confirm auth is working.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.