From 00281730bbc0a3fb4f9b9eb3f30c2fc427e188fc Mon Sep 17 00:00:00 2001 From: damocles Date: Thu, 21 May 2026 18:27:13 +0200 Subject: [PATCH] harness: sync agent icon to Forgejo user avatar on rebuild (closes #141) --- nix/templates/harness-base.nix | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/nix/templates/harness-base.nix b/nix/templates/harness-base.nix index 3040d45..e8f43d1 100644 --- a/nix/templates/harness-base.nix +++ b/nix/templates/harness-base.nix @@ -399,6 +399,61 @@ ''; }; + # One-shot: upload the agent's configured icon to its Forgejo user avatar + # so the icon shows up on commits / PRs / issue comments in the forge. + # Only runs when `/etc/hyperhive/icon.svg` is present (set via + # `hyperhive.icon`). No-op when the forge is unreachable or the icon + # is not set. *Always* exits 0. + systemd.services.forge-avatar-sync = { + description = "sync agent icon to Forgejo user avatar (best-effort)"; + wantedBy = [ "multi-user.target" ]; + after = [ "tea-login.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + path = [ + pkgs.curl + pkgs.coreutils + pkgs.python3 + ]; + script = '' + ICON=/etc/hyperhive/icon.svg + if [ ! -f "$ICON" ]; then + echo "forge-avatar-sync: no icon configured; skipping" + exit 0 + fi + FORGE_URL=${lib.escapeShellArg config.hyperhive.forge.url} + TOKEN_FILE="" + for f in /state/forge-token /agents/*/state/forge-token; do + if [ -f "$f" ]; then + TOKEN_FILE="$f" + break + fi + done + if [ -z "$TOKEN_FILE" ]; then + echo "forge-avatar-sync: no forge-token found; skipping" + exit 0 + fi + TOKEN=$(cat "$TOKEN_FILE") + # Base64-encode the SVG and wrap in a data URI. + IMAGE=$(base64 -w 0 < "$ICON") + DATA_URI="data:image/svg+xml;base64,$IMAGE" + RESP=$(curl -sf --max-time 10 \ + -X POST "$FORGE_URL/api/v1/user/avatar" \ + -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"image\":\"$DATA_URI\"}" \ + -w "\n%{http_code}" 2>/dev/null || true) + CODE=$(printf '%s' "$RESP" | tail -1) + if [ "$CODE" = "204" ] || [ "$CODE" = "200" ]; then + echo "forge-avatar-sync: avatar uploaded (HTTP $CODE)" + else + echo "forge-avatar-sync: upload returned HTTP $CODE — skipping (non-fatal)" + fi + ''; + }; + # Git is needed by claude's Bash tool (for the agent <-> manager config # request flow) and by hive-c0re's own setup_applied / setup_proposed. # The per-agent `applied//flake.nix` overrides `user.name` and