`bootstrap.sh --with-hermes` installs the Goose CLI, drops a Goose config pinning the OpenRouter provider + Nous Hermes model + the homelab MCP extension, symlinks `bin/hermes` and HERMES.md, and links HERMES.md as `.goosehints` so the persona is injected as the system prompt every session. `bin/hermes` decrypts `secrets/openrouter-api-key.yaml` via the existing `homelab secret` flow and execs `goose session`. `homelab client add --with-hermes` grants the new sops secret to the host's age_pubkey at finalize time (parallel to the existing shared-secrets grant). `client remove` revokes it. `operations/hermes-agent.md` covers the end-to-end flow, verification, troubleshooting, and queues one follow-up: the MCP server still runs SSE-only but Goose 1.x deprecated SSE — the Goose config targets `streamable_http` and the `homelab` extension won't connect until `mcp/server.py` migrates. The `developer` extension (shell + edit + `homelab` CLI) carries the agent in the meantime. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
37 lines
1.3 KiB
Bash
Executable File
37 lines
1.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# hermes — launch a Goose session pre-wired with the homelab persona,
|
|
# OpenRouter (Nous Hermes) provider, and the homelab MCP server.
|
|
#
|
|
# See operations/hermes-agent.md for the full onboarding flow.
|
|
|
|
set -euo pipefail
|
|
|
|
die() { echo "hermes: $*" >&2; exit 1; }
|
|
|
|
command -v goose >/dev/null \
|
|
|| die "goose binary not found — re-run bootstrap.sh with --with-hermes"
|
|
command -v homelab >/dev/null \
|
|
|| die "homelab CLI not found — is this client bootstrapped?"
|
|
|
|
# Decrypt OpenRouter API key.
|
|
# `homelab secret` re-execs via sudo for non-root users (age key is 0600 root).
|
|
SECRET_YAML=$(homelab secret openrouter-api-key 2>&1) || \
|
|
die "could not decrypt secrets/openrouter-api-key.yaml — this host probably
|
|
isn't a recipient yet. See operations/hermes-agent.md ('Granting the OpenRouter
|
|
key to a new host'). sops output:
|
|
${SECRET_YAML}"
|
|
|
|
API_KEY=$(printf '%s' "$SECRET_YAML" | python3 -c \
|
|
'import sys, yaml; print(yaml.safe_load(sys.stdin)["api_key"])')
|
|
|
|
case "$API_KEY" in
|
|
PLACEHOLDER_*|"")
|
|
die "openrouter-api-key.yaml still contains the placeholder; operator
|
|
must run \`sops secrets/openrouter-api-key.yaml\` on hubris to insert a real
|
|
\`sk-or-...\` key and push the change." ;;
|
|
esac
|
|
|
|
export OPENROUTER_API_KEY="$API_KEY"
|
|
exec goose session "$@"
|