Skip to content

Commit 43b8615

Browse files
feat: add hermes
1 parent d09a131 commit 43b8615

3 files changed

Lines changed: 221 additions & 0 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: Build Hermes Agent Image
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
hermesAgentTag:
7+
description: "Hermes Agent release tag"
8+
default: "0.9.0"
9+
required: true
10+
platforms:
11+
description: "Platforms"
12+
default: "linux/amd64,linux/arm64/v8"
13+
required: true
14+
pushLatest:
15+
description: "Push latest tag"
16+
default: "true"
17+
required: true
18+
19+
jobs:
20+
docker:
21+
runs-on: ubuntu-latest
22+
steps:
23+
- name: Checkout
24+
uses: actions/checkout@v5
25+
26+
- name: Set up QEMU
27+
uses: docker/setup-qemu-action@v3
28+
29+
- name: Set up Docker Buildx
30+
uses: docker/setup-buildx-action@v3
31+
32+
- name: Login to Docker Hub
33+
uses: docker/login-action@v3
34+
with:
35+
username: ${{ secrets.DOCKERHUB_USERNAME }}
36+
password: ${{ secrets.DOCKERHUB_PASSWORD }}
37+
38+
- name: Resolve Base Image
39+
id: resolve
40+
env:
41+
REQUESTED_IMAGE: nousresearch/hermes-agent:${{ github.event.inputs.hermesAgentTag }}
42+
FALLBACK_IMAGE: nousresearch/hermes-agent:latest
43+
run: |
44+
set -euo pipefail
45+
if docker buildx imagetools inspect "${REQUESTED_IMAGE}" >/dev/null 2>&1; then
46+
echo "base_image=${REQUESTED_IMAGE}" >> "${GITHUB_OUTPUT}"
47+
echo "resolved_base_tag=${{ github.event.inputs.hermesAgentTag }}" >> "${GITHUB_OUTPUT}"
48+
else
49+
echo "Requested image not found, falling back to latest: ${REQUESTED_IMAGE}"
50+
echo "base_image=${FALLBACK_IMAGE}" >> "${GITHUB_OUTPUT}"
51+
echo "resolved_base_tag=latest" >> "${GITHUB_OUTPUT}"
52+
fi
53+
54+
- name: Build Hermes Agent Image and Push
55+
uses: docker/build-push-action@v6
56+
with:
57+
context: .
58+
file: hermes-agent/Dockerfile
59+
platforms: ${{ github.event.inputs.platforms }}
60+
push: true
61+
build-args: |
62+
BASE_IMAGE=${{ steps.resolve.outputs.base_image }}
63+
tags: |
64+
1panel/hermes-agent:${{ github.event.inputs.hermesAgentTag }}
65+
cache-from: type=gha
66+
cache-to: type=gha,mode=max
67+
68+
- name: Push Latest Tag
69+
if: ${{ github.event.inputs.pushLatest == 'true' }}
70+
uses: docker/build-push-action@v6
71+
with:
72+
context: .
73+
file: hermes-agent/Dockerfile
74+
platforms: ${{ github.event.inputs.platforms }}
75+
push: true
76+
build-args: |
77+
BASE_IMAGE=${{ steps.resolve.outputs.base_image }}
78+
tags: |
79+
1panel/hermes-agent:latest
80+
cache-from: type=gha
81+
cache-to: type=gha,mode=max

hermes-agent/Dockerfile

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# syntax=docker/dockerfile:1.7
2+
3+
ARG BASE_IMAGE=nousresearch/hermes-agent:latest
4+
FROM ${BASE_IMAGE}
5+
6+
LABEL org.opencontainers.image.source="https://github.com/1Panel-dev/runtime" \
7+
org.opencontainers.image.title="1Panel Hermes Agent" \
8+
org.opencontainers.image.description="Hermes Agent image maintained by 1Panel with bundled messaging channel dependencies"
9+
10+
USER root
11+
12+
ENV HERMES_RUN_MODE=gateway \
13+
HERMES_DASHBOARD_HOST=0.0.0.0 \
14+
HERMES_DASHBOARD_PORT=9119 \
15+
HERMES_DASHBOARD_NO_OPEN=1 \
16+
VIRTUAL_ENV=/opt/hermes/.venv \
17+
PATH=/opt/hermes/.venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
18+
19+
COPY hermes-agent/docker-entrypoint.sh /usr/local/bin/1panel-hermes-entrypoint.sh
20+
21+
RUN chmod 755 /usr/local/bin/1panel-hermes-entrypoint.sh && \
22+
gosu hermes /usr/local/bin/uv pip install \
23+
--python /opt/hermes/.venv/bin/python \
24+
--no-cache-dir \
25+
aiohttp \
26+
httpx \
27+
cryptography \
28+
dingtalk-stream \
29+
lark-oapi \
30+
websockets \
31+
qrcode
32+
33+
WORKDIR /opt/data/workspace
34+
35+
ENTRYPOINT ["/usr/local/bin/1panel-hermes-entrypoint.sh"]

hermes-agent/docker-entrypoint.sh

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
HERMES_HOME="${HERMES_HOME:-/opt/data}"
5+
INSTALL_DIR="/opt/hermes"
6+
7+
bootstrap_files() {
8+
mkdir -p "$HERMES_HOME"/{cron,sessions,logs,hooks,memories,skills,skins,plans,workspace,home}
9+
10+
if [ ! -f "$HERMES_HOME/.env" ]; then
11+
cp "$INSTALL_DIR/.env.example" "$HERMES_HOME/.env"
12+
fi
13+
14+
if [ ! -f "$HERMES_HOME/config.yaml" ]; then
15+
cp "$INSTALL_DIR/cli-config.yaml.example" "$HERMES_HOME/config.yaml"
16+
fi
17+
18+
if [ ! -f "$HERMES_HOME/SOUL.md" ]; then
19+
cp "$INSTALL_DIR/docker/SOUL.md" "$HERMES_HOME/SOUL.md"
20+
fi
21+
22+
if [ -d "$INSTALL_DIR/skills" ]; then
23+
python3 "$INSTALL_DIR/tools/skills_sync.py"
24+
fi
25+
}
26+
27+
run_dashboard() {
28+
local host port
29+
30+
host="${HERMES_DASHBOARD_HOST:-0.0.0.0}"
31+
port="${HERMES_DASHBOARD_PORT:-9119}"
32+
33+
if [ "${HERMES_DASHBOARD_NO_OPEN:-1}" = "1" ]; then
34+
hermes dashboard --host "$host" --port "$port" --no-open
35+
else
36+
hermes dashboard --host "$host" --port "$port"
37+
fi
38+
}
39+
40+
run_both() {
41+
local gateway_pid dashboard_pid exit_code
42+
43+
hermes gateway run &
44+
gateway_pid=$!
45+
46+
run_dashboard &
47+
dashboard_pid=$!
48+
49+
trap 'kill "$gateway_pid" "$dashboard_pid" 2>/dev/null || true; wait "$gateway_pid" "$dashboard_pid" 2>/dev/null || true' INT TERM
50+
51+
wait -n "$gateway_pid" "$dashboard_pid"
52+
exit_code=$?
53+
kill "$gateway_pid" "$dashboard_pid" 2>/dev/null || true
54+
wait "$gateway_pid" "$dashboard_pid" 2>/dev/null || true
55+
return "$exit_code"
56+
}
57+
58+
if [ "$(id -u)" = "0" ]; then
59+
if [ -n "${HERMES_UID:-}" ] && [ "${HERMES_UID}" != "$(id -u hermes)" ]; then
60+
echo "Changing hermes UID to ${HERMES_UID}"
61+
usermod -u "${HERMES_UID}" hermes
62+
fi
63+
64+
if [ -n "${HERMES_GID:-}" ] && [ "${HERMES_GID}" != "$(id -g hermes)" ]; then
65+
echo "Changing hermes GID to ${HERMES_GID}"
66+
groupmod -g "${HERMES_GID}" hermes
67+
fi
68+
69+
actual_hermes_uid="$(id -u hermes)"
70+
if [ "$(stat -c %u "$HERMES_HOME" 2>/dev/null || echo missing)" != "$actual_hermes_uid" ]; then
71+
echo "$HERMES_HOME is not owned by $actual_hermes_uid, fixing"
72+
chown -R hermes:hermes "$HERMES_HOME"
73+
fi
74+
75+
exec gosu hermes "$0" "$@"
76+
fi
77+
78+
printf '\033[1;32m%s\033[0m\n' 'This image is maintained by 1Panel.'
79+
printf '\033[1;33m%s\033[0m\n' 'For support or issue discussion, please visit:'
80+
printf '\033[1;36m%s\033[0m\n' 'https://github.com/1Panel-dev/1Panel/discussions'
81+
82+
source "$INSTALL_DIR/.venv/bin/activate"
83+
bootstrap_files
84+
cd "$HERMES_HOME/workspace"
85+
86+
if [ "$#" -gt 0 ]; then
87+
exec hermes "$@"
88+
fi
89+
90+
case "${HERMES_RUN_MODE:-gateway}" in
91+
gateway)
92+
exec hermes gateway run
93+
;;
94+
dashboard)
95+
run_dashboard
96+
;;
97+
both)
98+
run_both
99+
;;
100+
*)
101+
echo "Unsupported HERMES_RUN_MODE: ${HERMES_RUN_MODE:-gateway}" >&2
102+
echo "Supported values: gateway, dashboard, both" >&2
103+
exit 1
104+
;;
105+
esac

0 commit comments

Comments
 (0)