relay: take the Hermes route from the request path
Renames the subscription to gitea-pr-comments (it handles one event; the old
gitea-events name promised more than it delivered) and drops --deliver.
Rather than move the hardcoded route from one constant to another, the relay
now reads it from the request path: POST /gitea/<route> forwards to
<base>/webhooks/<route>. The route name was the last thing tying this service
to a specific subscription, so a second Hermes route is now a `hermes webhook
subscribe <name>` plus a Gitea hook at /gitea/<name>, with no relay change --
previously it would also have needed a second relay URL baked in here.
The path segment is interpolated into an outbound URL, so it is validated
against ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$ and refused rather than sanitised
when it does not match. The path is matched raw and never URL-decoded, so
percent-encoded separators fail the charset check instead of surviving it;
requiring an alphanumeric first character also rejects "." and "..". Without
this, POST /gitea/..%2fadmin would let anything that can reach the relay
steer it at other Hermes endpoints. Tests cover traversal, encoded traversal,
embedded slashes, leading dot/dash, and the length bound, and assert nothing
reaches the stub Hermes in any of those cases.
Dropping --deliver leaves it at its default of `log`. The prompt tells her to
answer in the pull request, so the PR comment is the delivery and a Telegram
copy would only duplicate it; this also removes the hardcoded chat id that
was a third copy of TELEGRAM_HOME_CHANNEL.
Provisioning retires the pre-rename hook by its EXACT old URL rather than by
"points at the relay". Now that sibling hooks for other routes are the
intended pattern, a prefix match would delete them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S94o42aQ8VkBmEWvDem5xa
This commit is contained in:
@@ -42,7 +42,11 @@ in
|
||||
environment = {
|
||||
LISTEN_HOST = "0.0.0.0";
|
||||
LISTEN_PORT = "8645";
|
||||
HERMES_WEBHOOK_URL = "http://127.0.0.1:8644/webhooks/gitea-events";
|
||||
# Base only. The Hermes route rides in the request path
|
||||
# (/gitea/<route>), so this relay is not tied to any one subscription;
|
||||
# DEFAULT_ROUTE only serves the legacy bare /gitea path.
|
||||
HERMES_WEBHOOK_BASE = "http://127.0.0.1:8644/webhooks";
|
||||
DEFAULT_ROUTE = "gitea-pr-comments";
|
||||
MAX_BODY_BYTES = "1048576";
|
||||
};
|
||||
|
||||
@@ -76,12 +80,19 @@ in
|
||||
# pull_request_comment as a value distinct from issue_comment, so plain issue
|
||||
# comments do not reach the agent.
|
||||
#
|
||||
# A route carries exactly one prompt, so widening this list means either
|
||||
# branching inside the prompt on {action}/{issue.number}, or adding a second
|
||||
# subscription (and a second relay URL) for the other events. The Gitea-side
|
||||
# hook still sends the full event set at the relay; Hermes drops the
|
||||
# A route carries exactly one prompt, so widening this list means branching
|
||||
# inside the prompt on {action}, or adding a second subscription. The second
|
||||
# subscription is cheap now: the relay takes its target route from the
|
||||
# request path, so it is a new `hermes webhook subscribe <name>` plus a
|
||||
# Gitea hook pointing at /gitea/<name>, with no relay change at all. The
|
||||
# Gitea-side hook still sends the full event set; Hermes drops the
|
||||
# non-matching ones cheaply, before any LLM call.
|
||||
#
|
||||
# No --deliver: it defaults to `log`. The prompt tells her to answer in the
|
||||
# pull request, so the PR comment IS the delivery, and a Telegram copy would
|
||||
# just duplicate it. This also drops the hardcoded chat id that used to be a
|
||||
# third copy of TELEGRAM_HOME_CHANNEL.
|
||||
#
|
||||
# --script does the selection that MUST NOT be retunable at runtime.
|
||||
# hosts/mars/gitea-pr-comment-filter.py drops luna's own comments before
|
||||
# any LLM call, which is what stops the reply loop: the prompt tells her to
|
||||
@@ -127,8 +138,12 @@ in
|
||||
sleep 1
|
||||
done
|
||||
|
||||
podman exec hermes-agent hermes webhook remove gitea-pr-comments >/dev/null 2>&1 || true
|
||||
# gitea-events is the old name of this route (renamed to say what it
|
||||
# actually handles); removing it keeps a redeployed host from serving
|
||||
# both. The second remove is the idempotency step for the subscribe
|
||||
# below, not cleanup.
|
||||
podman exec hermes-agent hermes webhook remove gitea-events >/dev/null 2>&1 || true
|
||||
podman exec hermes-agent hermes webhook remove gitea-pr-comments >/dev/null 2>&1 || true
|
||||
# `set -eu` inside the container shell is load-bearing: without it a
|
||||
# missing prompt file makes `cat` fail, the command substitution yields
|
||||
# an empty string, and the subscription is created with an EMPTY prompt
|
||||
@@ -138,13 +153,12 @@ in
|
||||
set -eu
|
||||
prompt="$(cat /opt/data/prompts/gitea-pr-comment.md)"
|
||||
[ -n "$prompt" ] || { echo "gitea-pr-comment prompt is empty" >&2; exit 1; }
|
||||
hermes webhook subscribe gitea-events \
|
||||
hermes webhook subscribe gitea-pr-comments \
|
||||
--secret "$GITEA_HERMES_WEBHOOK_SECRET" \
|
||||
--description "Gitea PR comments -> L.U.N.A." \
|
||||
--events pull_request_comment \
|
||||
--script gitea-pr-comment-filter.py \
|
||||
--prompt "$prompt" \
|
||||
--deliver telegram --deliver-chat-id "15151223"
|
||||
--prompt "$prompt"
|
||||
'
|
||||
'';
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user