Skip to content

Commit 645b56b

Browse files
chaodu-agent張飛thepagent
authored
feat: add Hermes Agent as agent provider (#824)
* feat: add Hermes Agent as agent provider Add Hermes Agent (NousResearch) as a supported agent provider, enabling OAB to leverage Hermes's multi-provider OAuth infrastructure via ACP. Key benefits: - xAI Grok OAuth (SuperGrok $30/mo flat rate vs pay-per-token) - 30+ providers accessible through one agent - OAuth token lifecycle managed by Hermes (zero auth complexity for OAB) - Multi-modal support (TTS, image gen, video gen) via same OAuth token - Built-in fallback chains for provider resilience Files added: - Dockerfile.hermes: runtime image with Hermes Agent installed - docs/hermes.md: setup guide with auth and provider switching docs - config.toml.example: added Hermes agent config example Closes #823 * fix: pin install script, correct ACP invocation, add credential persistence docs - Dockerfile.hermes: pin install script to commit cc07e30f with SHA256 checksum verification instead of curl-pipe-bash from main - docs/hermes.md & config.toml.example: correct command from 'hermes --acp --stdio' to 'hermes-acp' (verified upstream) - docs/hermes.md: add PVC/volume mount guidance for credential persistence Addresses review findings from PR #824. * ci: add hermes variant to build & release matrix Adds Dockerfile.hermes to the build-image, merge-manifests, and promote-stable matrices so the ghcr.io/openabdev/openab-hermes image is published by CI alongside other agent variants. * fix: Dockerfile user/permission ordering and missing deps - Create agent user before WORKDIR so /home/agent has correct ownership - Set HERMES_HOME=/home/agent/.hermes during install so OAuth tokens are stored in agent user's home (not /root/.hermes) - Add ffmpeg for Hermes multi-modal support - chown /home/agent after all root operations complete Addresses review findings from 覺渡法師. * ci: add hermes to smoke-test matrix; clarify persistence docs - docker-smoke-test.yml: add Dockerfile.hermes variant - docs/hermes.md: clarify that Helm chart persistence covers .hermes by default; manual PVC only needed for non-Helm deploys * ci: add hermes to pr-preview variant choices * docs(chart): add hermes agent example to values.yaml The chart is generic over agents.<name>, so hermes already works, but adding a commented example makes discoverability easier and aligns with the docs/hermes.md Helm install instructions. * fix: add xz-utils for Node.js tar.xz extraction in hermes install * fix: symlink hermes-acp to /usr/local/bin for PATH visibility FHS root install only links 'hermes' to /usr/local/bin, but 'hermes-acp' stays in the venv. Add explicit symlink. * fix: recreate venv with system Python to avoid uv-managed path issues uv installs its own Python 3.11 at /root/.local/share/uv/python/ which is inaccessible to the agent user. Recreate venv with the image's system Python 3.12 after install script completes. * fix(docker): chmod uv-managed Python for non-root agent user The hermes install script uses uv which places Python 3.11 under /root/.local/share/uv/. The container runs as non-root user 'agent', causing 'Permission denied' when the venv tries to resolve its Python interpreter. Fix: chmod the uv directory and parent paths to be world-readable/executable. Also symlink hermes-acp to /usr/local/bin for PATH accessibility. Tested and verified working on orbstack with xai-oauth + grok-4.3. * docs(hermes): add xAI OAuth auth guide with port-forward for K8s pods * docs(hermes): emphasize SuperGrok paid subscription requirement * docs: add Hermes Agent to README table and config-reference * docs(readme): add Hermes to architecture diagram and feature list * docs(readme): fix diagram alignment --------- Co-authored-by: 張飛 <zhangfei@openab.dev> Co-authored-by: chaodufashi <chaodu-agent@users.noreply.github.com> Co-authored-by: thepagent <thepagent@users.noreply.github.com>
1 parent 5f9489d commit 645b56b

9 files changed

Lines changed: 242 additions & 14 deletions

File tree

.github/workflows/build.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ jobs:
7272
- { suffix: "-copilot", dockerfile: "Dockerfile.copilot", artifact: "copilot" }
7373
- { suffix: "-opencode", dockerfile: "Dockerfile.opencode", artifact: "opencode" }
7474
- { suffix: "-cursor", dockerfile: "Dockerfile.cursor", artifact: "cursor" }
75+
- { suffix: "-hermes", dockerfile: "Dockerfile.hermes", artifact: "hermes" }
7576
platform:
7677
- { os: linux/amd64, runner: ubuntu-latest }
7778
- { os: linux/arm64, runner: ubuntu-24.04-arm }
@@ -135,6 +136,7 @@ jobs:
135136
- { suffix: "-copilot", artifact: "copilot" }
136137
- { suffix: "-opencode", artifact: "opencode" }
137138
- { suffix: "-cursor", artifact: "cursor" }
139+
- { suffix: "-hermes", artifact: "hermes" }
138140
runs-on: ubuntu-latest
139141
permissions:
140142
contents: read
@@ -185,6 +187,7 @@ jobs:
185187
- { suffix: "-copilot" }
186188
- { suffix: "-opencode" }
187189
- { suffix: "-cursor" }
190+
- { suffix: "-hermes" }
188191
runs-on: ubuntu-latest
189192
permissions:
190193
contents: read

.github/workflows/docker-smoke-test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jobs:
2020
- { dockerfile: Dockerfile.copilot, suffix: "-copilot", agent: "copilot", agent_args: "--acp" }
2121
- { dockerfile: Dockerfile.opencode, suffix: "-opencode", agent: "opencode", agent_args: "acp" }
2222
- { dockerfile: Dockerfile.cursor, suffix: "-cursor", agent: "cursor-agent", agent_args: "acp" }
23+
- { dockerfile: Dockerfile.hermes, suffix: "-hermes", agent: "hermes-acp", agent_args: "" }
2324
runs-on: ubuntu-latest
2425
steps:
2526
- uses: actions/checkout@v6

.github/workflows/pr-preview.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ on:
1818
- copilot
1919
- cursor
2020
- gemini
21+
- hermes
2122
- opencode
2223
default: 'default'
2324

Dockerfile.hermes

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# --- Build stage ---
2+
FROM rust:1-bookworm AS builder
3+
WORKDIR /build
4+
COPY Cargo.toml Cargo.lock ./
5+
RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src
6+
COPY src/ src/
7+
RUN touch src/main.rs && cargo build --release
8+
9+
# --- Runtime stage ---
10+
FROM python:3.12-slim-bookworm
11+
12+
# Create agent user first so WORKDIR gets correct ownership
13+
RUN useradd -m -u 1000 agent
14+
15+
RUN apt-get update && apt-get install -y --no-install-recommends \
16+
ca-certificates curl procps ripgrep tini git ffmpeg xz-utils && \
17+
rm -rf /var/lib/apt/lists/*
18+
19+
# Install Hermes Agent — pinned to known commit with checksum verification
20+
# Root install uses FHS layout: binary at /usr/local/bin/hermes, code at /usr/local/lib/hermes-agent
21+
# HERMES_HOME points to agent user's data dir for OAuth tokens and config
22+
ARG HERMES_INSTALL_COMMIT=cc07e30f45267c00fac97ea5569c606aca5a1ffb
23+
ARG HERMES_INSTALL_SHA256=cb94b83b96cc924716bd1651411955da7495912ef68affe6788840e6cf147d41
24+
RUN curl -fsSL "https://raw.githubusercontent.com/NousResearch/hermes-agent/${HERMES_INSTALL_COMMIT}/scripts/install.sh" \
25+
-o /tmp/install-hermes.sh && \
26+
echo "${HERMES_INSTALL_SHA256} /tmp/install-hermes.sh" | sha256sum -c - && \
27+
HERMES_HOME=/home/agent/.hermes bash /tmp/install-hermes.sh && \
28+
rm /tmp/install-hermes.sh && \
29+
chmod -R a+rX /root/.local/share/uv && \
30+
chmod a+rx /root /root/.local /root/.local/share && \
31+
ln -sf /usr/local/lib/hermes-agent/venv/bin/hermes-acp /usr/local/bin/hermes-acp
32+
33+
# Install gh CLI
34+
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
35+
-o /usr/share/keyrings/githubcli-archive-keyring.gpg && \
36+
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
37+
> /etc/apt/sources.list.d/github-cli.list && \
38+
apt-get update && apt-get install -y --no-install-recommends gh && \
39+
rm -rf /var/lib/apt/lists/*
40+
41+
ENV HOME=/home/agent
42+
WORKDIR /home/agent
43+
44+
COPY --from=builder --chown=1000:1000 /build/target/release/openab /usr/local/bin/openab
45+
46+
RUN chown -R agent:agent /home/agent
47+
48+
USER agent
49+
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
50+
CMD pgrep -x openab || exit 1
51+
ENTRYPOINT ["tini", "--"]
52+
CMD ["openab", "run", "-c", "/etc/openab/config.toml"]

README.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,23 @@
44

55
![OpenAB banner](images/banner.jpg)
66

7-
A lightweight, secure, cloud-native ACP harness that bridges **Discord, Slack**, and any [Agent Client Protocol](https://github.com/anthropics/agent-protocol)-compatible coding CLI (Kiro CLI, Claude Code, Codex, Gemini, OpenCode, Copilot CLI, etc.) over stdio JSON-RPC — delivering the next-generation development experience. **Telegram, LINE, Feishu/Lark, Google Chat**, and other webhook-based platforms are supported via the standalone [Custom Gateway](gateway/).
7+
A lightweight, secure, cloud-native ACP harness that bridges **Discord, Slack**, and any [Agent Client Protocol](https://github.com/anthropics/agent-protocol)-compatible coding CLI (Kiro CLI, Claude Code, Codex, Gemini, OpenCode, Copilot CLI, Hermes, etc.) over stdio JSON-RPC — delivering the next-generation development experience. **Telegram, LINE, Feishu/Lark, Google Chat**, and other webhook-based platforms are supported via the standalone [Custom Gateway](gateway/).
88

99
🪼 **Join our community!** Come say hi on Discord — we'd love to have you: **[🪼 OpenAB — Official](https://discord.gg/DmbhfDZjQS)** 🎉
1010

1111
```
12-
┌──────────────┐ Gateway WS ┌──────────────┐ ACP stdio ┌──────────────┐
13-
│ Discord │◄─────────────►│ │──────────────►│ coding CLI │
14-
│ User │ │ openab │◄── JSON-RPC ──│ (acp mode) │
15-
├──────────────┤ Socket Mode │ (Rust) │ ──────────────
16-
│ Slack │◄─────────────►│ │
17-
│ User │ └──────┬───────┘
18-
├──────────────┤ │ WebSocket (outbound)
19-
│ Telegram │◄──webhook──┐ │
20-
│ User │ │ │
21-
├──────────────┤ ▼ ▼
22-
│ LINE │◄──webhook──┌──────────────────┐
23-
│ User │ │ Custom Gateway │
12+
┌──────────────┐ Gateway WS ┌──────────────┐ ACP stdio ┌──────────────────
13+
│ Discord │◄─────────────►│ │──────────────►│ coding CLI
14+
│ User │ │ openab │◄── JSON-RPC ──│ (acp mode)
15+
├──────────────┤ Socket Mode │ (Rust) │ ──────────────────┤
16+
│ Slack │◄─────────────►│ │ │ kiro-cli acp │
17+
│ User │ └──────┬───────┘ │ claude-agent-acp │
18+
├──────────────┤ │ WebSocket │ codex-acp │
19+
│ Telegram │◄──webhook──┐ │ (outbound) │ gemini --acp │
20+
│ User │ │ │ │ copilot --acp │
21+
├──────────────┤ ▼ ▼ │ hermes-acp │
22+
│ LINE │◄──webhook──┌──────────────────┐ │ opencode acp │
23+
│ User │ │ Custom Gateway │ └──────────────────┘
2424
├──────────────┤ │ (standalone) │
2525
│ Feishu/Lark │◄───WS──────│ │
2626
│ User │ │ │
@@ -38,7 +38,7 @@ A lightweight, secure, cloud-native ACP harness that bridges **Discord, Slack**,
3838

3939
- **Multi-platform** — supports Discord and Slack, run one or both simultaneously
4040
- **Custom Gateway** — extend to Telegram, LINE, Feishu/Lark, Google Chat, MS Teams via standalone [gateway](gateway/)
41-
- **Pluggable agent backend** — swap between Kiro CLI, Claude Code, Codex, Gemini, OpenCode, Copilot CLI via config
41+
- **Pluggable agent backend** — swap between Kiro CLI, Claude Code, Codex, Gemini, OpenCode, Copilot CLI, Hermes via config
4242
- **@mention trigger** — mention the bot in an allowed channel to start a conversation
4343
- **Thread-based multi-turn** — auto-creates threads; no @mention needed for follow-ups
4444
- **Multi-agent collaboration** — bot-to-bot messaging for coordinated workflows ([docs/multi-agent.md](docs/multi-agent.md))
@@ -168,6 +168,7 @@ The bot creates a thread. After that, just type in the thread — no @mention ne
168168
| OpenCode | `opencode acp` | Native | [docs/opencode.md](docs/opencode.md) |
169169
| Copilot CLI ⚠️ | `copilot --acp --stdio` | Native | [docs/copilot.md](docs/copilot.md) |
170170
| Cursor | `cursor-agent acp` | Native | [docs/cursor.md](docs/cursor.md) |
171+
| Hermes Agent | `hermes-acp` | Native | [docs/hermes.md](docs/hermes.md) |
171172

172173
> 🔧 Running multiple agents? See [docs/multi-agent.md](docs/multi-agent.md)
173174

charts/openab/values.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,27 @@ agents:
140140
# storageClass: ""
141141
# size: 1Gi
142142
# image: "ghcr.io/openabdev/openab-cursor:latest"
143+
# hermes:
144+
# command: hermes-acp
145+
# discord:
146+
# enabled: true
147+
# allowedChannels:
148+
# - "YOUR_CHANNEL_ID"
149+
# allowedUsers: []
150+
# allowBotMessages: "off"
151+
# trustedBotIds: []
152+
# workingDir: /home/agent
153+
# env: {}
154+
# envFrom: []
155+
# secretEnv: []
156+
# pool:
157+
# maxSessions: 10
158+
# sessionTtlHours: 24
159+
# persistence:
160+
# enabled: true
161+
# storageClass: ""
162+
# size: 1Gi
163+
# image: "ghcr.io/openabdev/openab-hermes"
143164
image: ""
144165
command: kiro-cli
145166
args:

config.toml.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ working_dir = "/home/agent"
103103
# working_dir = "/home/agent"
104104
# env = {} # Auth via: kubectl exec -it <pod> -- cursor-agent login
105105

106+
# [agent]
107+
# command = "hermes-acp"
108+
# working_dir = "/home/agent"
109+
# # Auth via: kubectl exec -it <pod> -- hermes auth add xai-oauth
110+
# # Supports 30+ providers (xAI Grok OAuth, Anthropic, OpenAI Codex, Gemini, etc.)
111+
# # Provider switching: kubectl exec -it <pod> -- hermes model
112+
106113
[pool]
107114
max_sessions = 10
108115
session_ttl_hours = 24

docs/config-reference.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ working_dir = "/home/node"
141141
command = "cursor-agent"
142142
args = ["acp", "--model", "auto", "--workspace", "/home/agent"]
143143
working_dir = "/home/agent"
144+
145+
# Hermes Agent
146+
[agent]
147+
command = "hermes-acp"
148+
working_dir = "/home/agent"
144149
```
145150

146151
---

docs/hermes.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Hermes Agent
2+
3+
[Hermes Agent](https://github.com/NousResearch/hermes-agent) by Nous Research supports ACP natively via the `hermes acp` subcommand (or the `hermes-acp` binary).
4+
5+
Hermes acts as a multi-provider inference gateway — it handles OAuth token lifecycle, credential storage, and provider routing so OAB agents don't need to manage auth directly.
6+
7+
## Docker Image
8+
9+
```bash
10+
docker build -f Dockerfile.hermes -t openab-hermes:latest .
11+
```
12+
13+
The image installs Hermes Agent via the official install script.
14+
15+
## Helm Install
16+
17+
```bash
18+
helm install openab openab/openab \
19+
--set agents.kiro.enabled=false \
20+
--set agents.hermes.discord.enabled=true \
21+
--set agents.hermes.discord.botToken="$DISCORD_BOT_TOKEN" \
22+
--set-string 'agents.hermes.discord.allowedChannels[0]=YOUR_CHANNEL_ID' \
23+
--set agents.hermes.image=ghcr.io/openabdev/openab-hermes:latest \
24+
--set agents.hermes.command=hermes-acp \
25+
--set agents.hermes.workingDir=/home/agent
26+
```
27+
28+
> Set `agents.kiro.enabled=false` to disable the default Kiro agent.
29+
30+
## Manual config.toml
31+
32+
```toml
33+
[agent]
34+
command = "hermes-acp"
35+
working_dir = "/home/agent"
36+
```
37+
38+
## Authentication
39+
40+
Hermes supports 30+ providers. Authenticate inside the pod:
41+
42+
```bash
43+
kubectl exec -it <pod> -- hermes auth add xai-oauth # xAI Grok (SuperGrok $30/mo)
44+
kubectl exec -it <pod> -- hermes auth add nous # Nous Portal
45+
kubectl exec -it <pod> -- hermes model # Interactive provider picker
46+
```
47+
48+
### xAI Grok OAuth (Recommended)
49+
50+
> ⚠️ **Requires an active [SuperGrok paid subscription](https://x.ai/grok) ($30/mo).** Auth will succeed without one, but the API silently returns empty responses — the bot appears to work but never replies.
51+
52+
xAI Grok OAuth uses a loopback redirect flow — the callback listener binds `127.0.0.1:56121` inside the pod. You need a port-forward so your browser's redirect reaches the pod:
53+
54+
```bash
55+
# Terminal 1: port-forward
56+
kubectl port-forward deployment/<your-deployment> 56121:56121
57+
58+
# Terminal 2: run auth
59+
kubectl exec -it deployment/<your-deployment> -- hermes auth add xai-oauth --no-browser
60+
```
61+
62+
1. Copy the printed authorize URL → open in your local browser
63+
2. Approve access on accounts.x.ai
64+
3. Browser redirects to `127.0.0.1:56121/callback` → port-forward delivers it to the pod
65+
4. Terminal shows "Login successful!"
66+
67+
After auth, set the default model:
68+
69+
```bash
70+
kubectl exec <pod> -- hermes config set model.provider xai-oauth
71+
kubectl exec <pod> -- hermes config set model.default grok-4.3
72+
```
73+
74+
> **Note:** Tokens are stored in `~/.hermes/auth.json` and auto-refresh in the background.
75+
76+
### Providers That Don't Need Port-Forward
77+
78+
| Provider | Auth Method |
79+
|----------|-------------|
80+
| Anthropic (Claude Pro/Max) | Paste-the-code flow |
81+
| OpenAI Codex (ChatGPT Plus/Pro) | Device code flow |
82+
| MiniMax, Nous Portal | Device code flow |
83+
| xAI Grok, Spotify | Loopback OAuth (port-forward required) |
84+
85+
### Supported Providers (via OAuth)
86+
87+
| Provider | Auth Command | Cost Model |
88+
|----------|-------------|------------|
89+
| xAI Grok | `hermes auth add xai-oauth` | SuperGrok subscription ($30/mo) |
90+
| OpenAI Codex | `hermes model` → OpenAI Codex | ChatGPT subscription |
91+
| GitHub Copilot | `hermes model` → GitHub Copilot | Copilot subscription |
92+
| Google Gemini | `hermes model` → Google Gemini (OAuth) | Free tier available |
93+
| Anthropic | `hermes model` → Anthropic | Claude Max + extra credits |
94+
| Nous Portal | `hermes auth add nous` | Nous subscription |
95+
96+
### Supported Providers (via API Key)
97+
98+
Any provider can also be configured with an API key via environment variables:
99+
100+
```toml
101+
[agent]
102+
command = "hermes-acp"
103+
working_dir = "/home/agent"
104+
env = { XAI_API_KEY = "${XAI_API_KEY}" }
105+
```
106+
107+
## Provider Switching
108+
109+
Switch providers without restarting the pod:
110+
111+
```bash
112+
kubectl exec -it <pod> -- hermes model
113+
```
114+
115+
## Credential Persistence
116+
117+
Hermes stores OAuth tokens in `~/.hermes/`. The OpenAB Helm chart's default persistence covers this automatically (PVC mounted at `workingDir`).
118+
119+
If deploying manually (without the Helm chart), mount persistent storage at `/home/agent` or `/home/agent/.hermes`:
120+
121+
```yaml
122+
volumes:
123+
- name: hermes-credentials
124+
persistentVolumeClaim:
125+
claimName: hermes-credentials-pvc
126+
volumeMounts:
127+
- name: hermes-credentials
128+
mountPath: /home/agent/.hermes
129+
```
130+
131+
## Advantages
132+
133+
- **Cost**: SuperGrok $30/mo flat rate vs pay-per-token API pricing
134+
- **Multi-provider**: 30+ providers accessible through one agent
135+
- **Zero auth complexity**: Hermes handles OAuth + token refresh
136+
- **Multi-modal**: TTS, image gen, video gen via the same OAuth token
137+
- **Fallback chains**: Auto-switch providers on failure

0 commit comments

Comments
 (0)