Compare commits

..

13 Commits

Author SHA1 Message Date
62fa78b97f fix(hermes-workspace): add HOST=0.0.0.0 for Traefik reachability, drop redundant nodejs install
The base hermets-agent image already ships Node.js; apt-get nodejs
could conflict or downgrade it. Only install tmux and curl.

Add ENV HOST=0.0.0.0 so the workspace listens on all interfaces
(default 127.0.0.1 makes it unreachable via Traefik reverse proxy).
Add ENV NODE_ENV=production for production-mode optimizations.
2026-05-20 14:07:26 -04:00
cd9bda57c3 Revert "fix(hermes-workspace): add HOST/ENV vars, drop redundant nodejs install"
This reverts commit 735ddcb555.
2026-05-20 14:05:51 -04:00
735ddcb555 fix(hermes-workspace): add HOST/ENV vars, drop redundant nodejs install
- Add ENV HOST=0.0.0.0 so workspace listens on all interfaces
  (required for Traefik reverse proxy to reach it)
- Add ENV NODE_ENV=production for production-mode optimizations
- Remove apt-get install of nodejs (already in base image,
  apt version would be older and could conflict)
- Only install tmux and curl in the workspace layer
- entrypoint-combined.sh: rewritten with proper logging,
  graceful shutdown, configurable gateway health check
2026-05-20 14:05:41 -04:00
817f1cbcc2 feat(hermes): add Traefik routing + Authelia auth for dashboard 2026-05-20 14:04:05 -04:00
8e09e5bdfe Revert "feat(hermes): drop fork overlay, use upstream base image for v0.12.0+ kanban"
This reverts commit d94014f19a.
2026-05-19 21:29:47 -04:00
d94014f19a feat(hermes): drop fork overlay, use upstream base image for v0.12.0+ kanban 2026-05-19 21:23:09 -04:00
6b506163e9 feat: add combined Hermes Workspace image with Swarm worker support
New directory ai/hermes-workspace/ with:
- Dockerfile (multi-stage): builds workspace web UI from source,
  overlays our Hermes fork, installs tmux for Swarm workers
- entrypoint-combined.sh: starts workspace UI, waits for gateway
- himalaya-ro.sh: read-only Himalaya wrapper (shared from hermes/)

Existing ai/hermes/ Dockerfile preserved unchanged as fallback.

compose.yml changes:
- Add HERMES_DASHBOARD=1 + healthcheck to hermes service
- Add hermes-workspace service using combined image
  (build context: ./hermes-workspace, SSH build)
  - Connects to hermes:8642 (gateway) + :9119 (dashboard)
  - Shares Hermes data volume for config/sessions/skills
  - Traefik on workspace.lazyworkhorse.net (port 3000)
  - Networks: ai_backend + ai_net
2026-05-19 20:50:08 -04:00
d3f2e3b7b9 Merge pull request 'feat: add Syncthing service for Hermes org-file sync' (#30) from feat/syncthing-org-sync into master
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Build ollama (gfx906) / build (push) Has been cancelled
Reviewed-on: #30
2026-05-19 00:28:59 +00:00
6a44120b1a Fixed syncthing dir path
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-18 20:25:18 -04:00
38a1451689 Merge branch 'master' into feat/syncthing-org-sync 2026-05-14 22:24:19 -04:00
f9fb28d560 fix: route Syncthing web UI through Traefik with HTTPS
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-14 21:40:00 -04:00
bcc4b6d157 feat: add Syncthing service for Hermes org-file sync
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-14 21:35:31 -04:00
8d1ae7e632 Remove the unsuported gitea action off 2026-05-13 13:11:11 -04:00
6 changed files with 339 additions and 184 deletions

View File

@@ -1,35 +1,4 @@
version: "3.8"
# ── Hermes Worker Template ──────────────────────────────────
# Used by paperclip-worker-* Hermes containers via YAML anchor.
# Each worker = one isolated Hermes agent for a Paperclip employee.
# Override at service level: container_name, API_SERVER_PORT,
# API_SERVER_KEY, DISCORD_BOT_TOKEN, volumes.
# Workers have NO GPU — they use OpenCode Go or remote providers.
x-hermes-worker: &hermes-worker
build:
context: ./hermes
ssh:
- default
entrypoint: ["/bin/bash", "-c",
"bash /opt/data/hermes-tools/install.sh && exec /usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh \"$@\"",
"hermes-entrypoint"]
command: gateway run
restart: always
environment:
API_SERVER_ENABLED: "true"
API_SERVER_HOST: "0.0.0.0"
OLLAMA_HOST: "http://ollama:11434"
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
# Each worker needs its own OpenCode Go API key in .env
OPENCODE_GO_API_KEY: ${OPENCODE_GO_API_KEY}
GATEWAY_ALLOW_ALL_USERS: "true"
TZ: "America/Montreal"
networks:
ai_backend:
# NO devices — workers are CPU-only, no GPU passthrough
# ─────────────────────────────────────────────────────────────
services:
# webui:
@@ -83,8 +52,16 @@ services:
- ROCR_VISIBLE_DEVICES=0,1
- HSA_ENABLE_SDMA=0
- TZ=America/Montreal
# Hermes Workspace dashboard (port 9119) — enables multi-agent web UI
- HERMES_DASHBOARD=1
- HERMES_DASHBOARD_HOST=0.0.0.0
- HERMES_DASHBOARD_PORT=9119
volumes:
- /mnt/HoardingCow_docker_data/Hermes/data:/opt/data
# Syncthing-shared org files — read-only view of user's agenda
- /mnt/HoardingCow_docker_data/Syncthing/telos-ro:/opt/data/telos-ro:ro
# Syncthing-shared inbox — write tasks here, they sync to user's laptop
- /mnt/HoardingCow_docker_data/Syncthing/telos-rw:/opt/data/telos-rw:rw
devices:
- /dev/kfd:/dev/kfd
- /dev/dri:/dev/dri
@@ -93,6 +70,65 @@ services:
- "26"
networks:
- ai_backend
- ai_net
labels:
- "traefik.enable=true"
- "traefik.docker.network=ai_net"
# Router for HTTP + redirect to HTTPS
- "traefik.http.routers.hermes-web-http.rule=Host(`hermes.lazyworkhorse.net`)"
- "traefik.http.routers.hermes-web-http.entrypoints=web"
- "traefik.http.routers.hermes-web-http.middlewares=redirect-to-https"
# Router for HTTPS with TLS — protected by Authelia
- "traefik.http.routers.hermes-web-https.rule=Host(`hermes.lazyworkhorse.net`)"
- "traefik.http.routers.hermes-web-https.entrypoints=websecure"
- "traefik.http.routers.hermes-web-https.tls=true"
- "traefik.http.routers.hermes-web-https.tls.certresolver=njalla"
- "traefik.http.routers.hermes-web-https.middlewares=hermes-auth"
# Authelia forwardAuth
- "traefik.http.middlewares.hermes-auth.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.lazyworkhorse.net/"
- "traefik.http.middlewares.hermes-auth.forwardauth.trustforwardheader=true"
- "traefik.http.middlewares.hermes-auth.forwardauth.authresponseheaders=X-Forwarded-User,X-Forwarded-Groups"
# Service Loadbalancer (dashboard port 9119)
- "traefik.http.services.hermes-web.loadbalancer.server.port=9119"
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:8642/health && curl -fsS http://localhost:9119/api/status || exit 1"]
interval: 15s
timeout: 5s
retries: 5
start_period: 60s
syncthing:
image: syncthing/syncthing:latest
container_name: syncthing
hostname: syncthing
restart: always
ports:
- "8384:8384"
- "22000:22000"
- "21027:21027/udp"
environment:
- TZ=America/Montreal
volumes:
- /mnt/HoardingCow_docker_data/Syncthing/config:/var/syncthing/config
- /mnt/HoardingCow_docker_data/Syncthing/telos-ro:/telos-ro
- /mnt/HoardingCow_docker_data/Syncthing/telos-rw:/telos-rw
networks:
- ai_backend
- ai_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.syncthing-http.rule=Host(`syncthing.lazyworkhorse.net`)"
- "traefik.http.routers.syncthing-http.entrypoints=web"
- "traefik.http.routers.syncthing-http.middlewares=redirect-to-https"
- "traefik.http.routers.syncthing-https.rule=Host(`syncthing.lazyworkhorse.net`)"
- "traefik.http.routers.syncthing-https.entrypoints=websecure"
- "traefik.http.routers.syncthing-https.tls=true"
- "traefik.http.routers.syncthing-https.tls.certresolver=njalla"
- "traefik.http.services.syncthing.loadbalancer.server.port=8384"
ollama:
build:
@@ -127,22 +163,44 @@ services:
- "303"
- "26"
# ── Paperclip Worker Hermes Agents ──────────────────────────
# Each worker is an isolated Hermes agent for a Paperclip employee.
# Add new workers with: ./scripts/provision-hermes-worker.sh <name> <discord-token>
# The API server key and port are generated automatically.
# Workers are CPU-only — they use OpenCode Go or remote providers.
# ── Hermes Workspace (combined image) ────────────────────────
# Web UI + Swarm worker support. Uses custom combined image with
# our Hermes fork + workspace web UI + tmux for Swarm workers.
hermes-workspace:
build:
context: ./hermes-workspace
ssh:
- default
container_name: hermes-workspace
restart: unless-stopped
depends_on:
hermes:
condition: service_healthy
environment:
HERMES_API_URL: http://hermes:8642
HERMES_DASHBOARD_URL: http://hermes:9119
HERMES_API_TOKEN: hermes_local_key
HERMES_PASSWORD: ${HERMES_WORKSPACE_PASSWORD:?must be set}
COOKIE_SECURE: "1"
volumes:
- /mnt/HoardingCow_docker_data/Hermes/data:/opt/data
networks:
- ai_backend
- ai_net
labels:
- "traefik.enable=true"
- "traefik.docker.network=ai_net"
# ── Worker Template (commented — uncomment + configure to activate) ──
# hermes-worker-1:
# <<: *hermes-worker
# container_name: hermes-worker-1
# environment:
# API_SERVER_PORT: "8651"
# API_SERVER_KEY: "generated-by-provision-script"
# DISCORD_BOT_TOKEN: ${WORKER_1_DISCORD_BOT_TOKEN}
# volumes:
# - /mnt/HoardingCow_docker_data/Hermes/worker-1:/opt/data
- "traefik.http.routers.workspace-http.rule=Host(`workspace.lazyworkhorse.net`)"
- "traefik.http.routers.workspace-http.entrypoints=web"
- "traefik.http.routers.workspace-http.middlewares=redirect-to-https"
- "traefik.http.routers.workspace-https.rule=Host(`workspace.lazyworkhorse.net`)"
- "traefik.http.routers.workspace-https.entrypoints=websecure"
- "traefik.http.routers.workspace-https.tls=true"
- "traefik.http.routers.workspace-https.tls.certresolver=njalla"
- "traefik.http.services.workspace.loadbalancer.server.port=3000"
# ─────────────────────────────────────────────────────────────
networks:

View File

@@ -0,0 +1,129 @@
# syntax=docker/dockerfile:1
# Hermes Agent + Hermes Workspace — combined image
# Builds on top of official image + our forked source + workspace UI.
# Supports Swarm Mode (tmux workers) in a single container.
# Requires Docker BuildKit. Pass SSH agent for git clone:
# docker compose build hermes-workspace
# ---------- Stage 1: Build Hermes Workspace (web UI) ----------
FROM node:22-slim AS workspace-build
WORKDIR /app
# Install pnpm and git
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates git curl \
&& rm -rf /var/lib/apt/lists/* \
&& corepack enable
# Clone workspace source (pinned to a known-good commit on main)
RUN git clone --depth 1 --branch main \
https://github.com/outsourc-e/hermes-workspace.git /app/workspace-src \
&& rm -rf /app/workspace-src/.git
WORKDIR /app/workspace-src
# Install deps and build
RUN pnpm install --frozen-lockfile && pnpm build
# ---------- Stage 2: Hermes Agent + Workspace runtime ----------
FROM nousresearch/hermes-agent:latest
# ---------- Install tmux for Swarm workers + curl for health checks ----------
# Note: Node.js is already shipped with the base hermets-agent image; apt's nodejs
# would be older and could conflict. Only add what's missing.
USER root
RUN apt-get update && apt-get install -y --no-install-recommends \
tmux curl \
&& rm -rf /var/lib/apt/lists/*
# ---------- Overlay our forked Hermes source ----------
RUN --mount=type=ssh \
mkdir -p /root/.ssh && \
ssh-keyscan -p 2222 code.lazyworkhorse.net >> /root/.ssh/known_hosts 2>/dev/null && \
cd /tmp && \
GIT_SSH_COMMAND='ssh -p 2222 -o StrictHostKeyChecking=no' \
git clone --depth 1 --branch main \
git@code.lazyworkhorse.net:gortium/hermes-agent.git fork && \
rsync -a --delete fork/ /opt/hermes/ \
--exclude node_modules \
--exclude .venv \
--exclude .git && \
rm -rf /tmp/fork /root/.ssh/
# ---------- Rebuild web UI ----------
RUN cd /opt/hermes && npm run build
# ---------- Reinstall Python package ----------
RUN . /opt/hermes/.venv/bin/activate && \
uv pip install --no-cache-dir --no-deps -e /opt/hermes
# ---------- Extra system deps ----------
RUN apt-get update && apt-get install -y --no-install-recommends \
libportaudio2 ca-certificates poppler-utils imagemagick \
texlive-latex-base texlive-latex-extra texlive-fonts-recommended \
texlive-xetex texlive-science \
qemu-user-static binfmt-support emacs-nox \
&& rm -rf /var/lib/apt/lists/*
# ---------- UV ----------
COPY --chmod=0755 --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/
# ---------- Piper TTS ----------
RUN . /opt/hermes/.venv/bin/activate && \
uv pip install --no-cache-dir piper-tts sounddevice numpy && \
mkdir -p /opt/hermes/.venv/share/piper/voices
RUN /opt/hermes/.venv/bin/python3 /dev/stdin << 'PYEOF'
import urllib.request
base = '/opt/hermes/.venv/share/piper/voices'
url = 'https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/ryan/high/en_US-ryan-high.onnx'
urllib.request.urlretrieve(url, base + '/en_US-ryan-high.onnx')
urllib.request.urlretrieve(url + '.json', base + '/en_US-ryan-high.onnx.json')
PYEOF
# ---------- Install Himalaya email CLI ----------
RUN /opt/hermes/.venv/bin/python3 /dev/stdin << 'PYEOF'
import urllib.request, tarfile, os, shutil
url = 'https://github.com/pimalaya/himalaya/releases/download/v1.2.0/himalaya.x86_64-linux.tgz'
tgz = '/tmp/himalaya.tgz'
urllib.request.urlretrieve(url, tgz)
with tarfile.open(tgz) as t:
t.extractall('/tmp')
shutil.move('/tmp/himalaya', '/usr/local/bin/himalaya')
os.chmod('/usr/local/bin/himalaya', 0o755)
os.remove(tgz)
print('himalaya v1.2.0 installed')
PYEOF
# ---------- Install himalaya-ro wrapper ----------
COPY --chmod=0755 himalaya-ro.sh /usr/local/bin/himalaya-ro
# ---------- Copy Hermes Workspace build artifacts ----------
COPY --from=workspace-build --chown=hermes:hermes \
/app/workspace-src/dist /workspace/dist
COPY --from=workspace-build --chown=hermes:hermes \
/app/workspace-src/node_modules /workspace/node_modules
COPY --from=workspace-build --chown=hermes:hermes \
/app/workspace-src/package.json /workspace/
COPY --from=workspace-build --chown=hermes:hermes \
/app/workspace-src/server-entry.js /workspace/
COPY --from=workspace-build --chown=hermes:hermes \
/app/workspace-src/skills /workspace/skills
COPY --chmod=0755 entrypoint-combined.sh /usr/local/bin/entrypoint-combined.sh
# ---------- Runtime ----------
USER hermes
ENV HERMES_HOME=/opt/data
ENV PATH="/opt/data/.local/bin:${PATH}"
ENV CHROME_EXECUTABLE=/opt/hermes/.playwright/chromium/chrome-linux/chrome
ENV HOST=0.0.0.0
ENV NODE_ENV=production
RUN chown -R hermes:hermes /opt/hermes/tools /opt/hermes/toolsets.py
VOLUME [ "/opt/data" ]
EXPOSE 8642 9119 3000
ENTRYPOINT ["/usr/bin/tini", "-g", "--", "/usr/local/bin/entrypoint-combined.sh"]

View File

@@ -0,0 +1,33 @@
#!/bin/bash
set -e
# ── Hermes Workspace + Swarm Worker Entrypoint ──
# Starts Hermes Workspace web UI (port 3000) and makes
# hermes CLI + tmux available for Swarm workers.
# The Hermes gateway runs in a separate container (hermes:8642).
# Swarm workers spawned here connect to the gateway via HTTP.
# ──────────────────────────────────────────────────────────
# Install custom tools from persistent volume
if [ -f /opt/data/hermes-tools/install.sh ]; then
bash /opt/data/hermes-tools/install.sh || true
fi
# Wait for Hermes gateway to be healthy before starting workspace
if [ -n "${HERMES_API_URL:-}" ]; then
echo "Waiting for Hermes gateway..."
for i in $(seq 1 30); do
if curl -fsS "${HERMES_API_URL}/health" >/dev/null 2>&1; then
echo "Gateway healthy after ${i}s"
break
fi
if [ "$i" -eq 30 ]; then
echo "WARNING: Gateway not healthy after 30s, starting workspace anyway"
fi
sleep 1
done
fi
# Start Hermes Workspace in foreground
cd /workspace
exec node --max-old-space-size=2048 server-entry.js

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────
# himalaya-ro — Read-only wrapper for himalaya
#
# Blocks destructive commands and logs audit trail.
# Pass-through for read-only commands (list, read, search).
#
# Usage: himalaya-ro [options] <command> [args...]
#
# Install: place in PATH before the real himalaya, or use
# `ln -sf himalaya-ro /usr/local/bin/himalaya`
# ─────────────────────────────────────────────────────────────
set -o pipefail
# ── Configuration ───────────────────────────────────────────
HIMALAYA_BIN="${HIMALAYA_BIN:-/usr/local/bin/himalaya}"
AUDIT_LOG="${HIMALAYA_AUDIT_LOG:-/var/log/himalaya-audit.log}"
# ── Destructive commands we block ──────────────────────────
BLOCKED_CMDS=(
"message move"
"message delete"
"message copy"
"flag add"
"flag remove"
"folder create"
"folder delete"
"folder rename"
"template send"
"account configure"
"account delete"
)
# ── Determine the subcommand being invoked ─────────────────
# Strip leading options (--account, --output, etc.) to find the verb
ARGS=()
SKIP_NEXT=false
for arg in "$@"; do
if $SKIP_NEXT; then
SKIP_NEXT=false
continue
fi
if [[ "$arg" == --* ]]; then
case "$arg" in
--account|--output|--page|--page-size|--folder|--color|--format)
SKIP_NEXT=true ;;
esac
continue
fi
ARGS+=("$arg")
done
# Build subcommand string and check against blocklist
CMD_STR=""
for ((i=0; i<${#ARGS[@]}; i++)); do
if [ -z "$CMD_STR" ]; then
CMD_STR="${ARGS[$i]}"
else
CMD_STR="$CMD_STR ${ARGS[$i]}"
fi
for blocked in "${BLOCKED_CMDS[@]}"; do
if [[ "$CMD_STR" == "$blocked" ]]; then
TS=$(date '+%Y-%m-%d %H:%M:%S')
echo "[AUDIT] $TS BLOCKED: himalaya $*" >> "$AUDIT_LOG"
echo "ERROR: Command 'himalaya $CMD_STR ...' is blocked by read-only policy." >&2
echo " Audit log: $AUDIT_LOG" >&2
exit 100
fi
done
done
# ── Allow pass-through ─────────────────────────────────────
exec "$HIMALAYA_BIN" "$@"

View File

@@ -1,135 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# ── Hermes Worker Provisioner ──────────────────────────────
# Adds a new Paperclip Hermes worker to the ai compose stack.
#
# Usage:
# ./provision-hermes-worker.sh <name> <discord_bot_token_var>
#
# Example:
# ./provision-hermes-worker.sh worker-1 WORKER_1_DISCORD_BOT_TOKEN
#
# The script APPENDS only — never modifies or removes existing
# content, even commented lines.
#
# Post-provision steps (manual):
# 1. Add secrets to agenix .env file
# 2. systemctl restart ai_stack.service
# 3. Configure Paperclip agent
# ─────────────────────────────────────────────────────────────
NAME="${1:?Usage: $0 <name> <discord_bot_token_var>}"
TOKEN_VAR="${2:?Usage: $0 <name> <discord_bot_token_var>}"
# ── Paths ───────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
COMPOSE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
COMPOSE_FILE="${COMPOSE_DIR}/compose.yml"
# Each Hermes worker gets its own volume on the NFS HoardingCow
VOLUME_BASE="/mnt/HoardingCow_docker_data/Hermes"
VOLUME_DIR="${VOLUME_BASE}/${NAME}"
# The Hermes container runs as UID 10000 (hermes user from Dockerfile)
HERMES_UID=10000
# ── Validation ──────────────────────────────────────────────
if ! [ -f "$COMPOSE_FILE" ]; then
echo "❌ compose.yml not found at $COMPOSE_FILE"
exit 1
fi
if grep -q "^ ${NAME}:" "$COMPOSE_FILE"; then
echo "❌ Service '${NAME}' already exists in ${COMPOSE_FILE}"
exit 1
fi
# ── Generate unique API key ─────────────────────────────────
# Used by Paperclip to authenticate against this worker's
# Hermes API server (/v1/chat/completions)
API_KEY="pc_worker_$(openssl rand -hex 16)"
# ── Find next available API port ────────────────────────────
# Workers get sequential ports starting at 8650.
# Scans compose.yml for existing API_SERVER_PORT values and
# picks the next one.
BASE_PORT=8650
MAX_PORT=0
while IFS= read -r line; do
port="${line#*API_SERVER_PORT: \"}"
port="${port%%\"*}"
if [ -n "$port" ] && [ "$port" -gt "$MAX_PORT" ]; then
MAX_PORT="$port"
fi
done < <(grep -oP 'API_SERVER_PORT:\s*"\d+"' "$COMPOSE_FILE" 2>/dev/null)
NEW_PORT=$((MAX_PORT + 1))
if [ "$NEW_PORT" -lt "$BASE_PORT" ]; then
NEW_PORT=$BASE_PORT
fi
# ── Create volume directory (on NFS) ────────────────────────
echo "📁 Creating volume directory: ${VOLUME_DIR}"
mkdir -p "$VOLUME_DIR"
# Hermes container runs as UID 10000 — set ownership so the
# container can write its config, sessions, skills
if command -v chown &>/dev/null; then
chown -R "${HERMES_UID}:${HERMES_UID}" "$VOLUME_DIR" 2>/dev/null || \
echo "⚠ Could not chown ${VOLUME_DIR} — run with sudo if needed"
fi
# Make it group-readable for debugging
chmod 755 "$VOLUME_DIR" 2>/dev/null || true
# ── Append service to compose.yml ───────────────────────────
echo "📝 Appending service '${NAME}' to compose.yml ..."
TMPFILE=$(mktemp)
awk -v name="$NAME" \
-v port="$NEW_PORT" \
-v api_key="$API_KEY" \
-v token_var="$TOKEN_VAR" \
'
# Insert new worker service block just before the networks: section
/^networks:/ {
print ""
print " " name ":"
print " <<: *hermes-worker"
print " container_name: " name
print " environment:"
print " API_SERVER_PORT: \"" port "\""
print " API_SERVER_KEY: \"" api_key "\""
print " DISCORD_BOT_TOKEN: ${" token_var "}"
print " volumes:"
print " - /mnt/HoardingCow_docker_data/Hermes/" name ":/opt/data"
print ""
}
{ print }
' "$COMPOSE_FILE" > "$TMPFILE" && mv "$TMPFILE" "$COMPOSE_FILE"
# ── Done ────────────────────────────────────────────────────
echo ""
echo "✅ Worker '${NAME}' provisioned successfully"
echo ""
echo "────────────────────────────────────────────"
echo " NEXT STEPS"
echo "────────────────────────────────────────────"
echo ""
echo "1. Add secrets to the agenix .env stack file:"
echo ""
echo " # ${NAME}"
echo " ${TOKEN_VAR}=<paste-discord-bot-token-here>"
echo ""
echo "2. Restart the AI stack:"
echo ""
echo " systemctl restart ai_stack.service"
echo ""
echo "3. In Paperclip, create an agent with HTTP adapter:"
echo ""
echo " Endpoint: http://${NAME}:${NEW_PORT}/v1/chat/completions"
echo " API Key: ${API_KEY}"
echo ""
echo "────────────────────────────────────────────"

View File

@@ -8,13 +8,10 @@ services:
- USER_GID=1000
- GITEA__server__ROOT_URL=https://code.lazyworkhorse.net
- GITEA__actions__ENABLED=true
- GITEA__actions__DEFAULT_ACTIONS_URL=off
- SSH_PORT=2222
- SSH_LISTEN_PORT=2222
# Enable Gitea Actions (act_runner required on host)
- GITEA__actions__ENABLED=true
# Don't fetch actions from GitHub (offline mode + local only)
- GITEA__actions__DEFAULT_ACTIONS_URL=off
volumes:
- /mnt/HoardingCow_docker_data/Gitea:/data
networks: