feat(hermes): expose persistent agent TUI
Some checks failed
Tests / Declarative: Post Actions failed: 2, passed: 153

This commit is contained in:
jenkins 2026-08-08 22:05:39 -03:00
parent 5159c98d64
commit b21843e35a
5 changed files with 104 additions and 5 deletions

View File

@ -186,8 +186,9 @@ data:
START-HERE.md: | START-HERE.md: |
# Agent Hermes # Agent Hermes
Select the Cassandra project and state the outcome you want. Hermes will The authenticated root of agent.hermes.bstein.dev opens the persistent
classify its difficulty, choose Codex or Claude Code, preserve the task on Herdr terminal interface. Give Hermes the outcome you want and it will
classify the difficulty, choose Codex or Claude Code, preserve the task on
the Cassandra board, supervise the worker through Herdr, and synthesize the the Cassandra board, supervise the worker through Herdr, and synthesize the
evidence. The first native Codex worker requires one device-code login; evidence. The first native Codex worker requires one device-code login;
subsequent sessions persist on the agent volume. subsequent sessions persist on the agent volume.

View File

@ -24,7 +24,7 @@ spec:
ai.bstein.dev/execution: Herdr-supervised Codex and Claude Code ai.bstein.dev/execution: Herdr-supervised Codex and Claude Code
ai.bstein.dev/model-policy: difficulty-aware low through xhigh, cross-provider fallback ai.bstein.dev/model-policy: difficulty-aware low through xhigh, cross-provider fallback
ai.bstein.dev/placement: rpi5 preferred; Jetson deferred until state storage is available ai.bstein.dev/placement: rpi5 preferred; Jetson deferred until state storage is available
ai.bstein.dev/config-rev: "20260808-herdr-coordinator" ai.bstein.dev/config-rev: "20260808-herdr-browser-tui"
vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: hermes-agent vault.hashicorp.com/role: hermes-agent
vault.hashicorp.com/agent-inject-secret-anthropic-token: kv/data/atlas/hermes/agent-tokens vault.hashicorp.com/agent-inject-secret-anthropic-token: kv/data/atlas/hermes/agent-tokens
@ -155,6 +155,14 @@ spec:
chmod 0755 "${tools}/bin/herdr.tmp" chmod 0755 "${tools}/bin/herdr.tmp"
mv "${tools}/bin/herdr.tmp" "${tools}/bin/herdr" mv "${tools}/bin/herdr.tmp" "${tools}/bin/herdr"
fi fi
ttyd_version="$("${tools}/bin/ttyd" --version 2>/dev/null || true)"
case "${ttyd_version}" in *1.7.7*) ttyd_ready=1 ;; *) ttyd_ready=0 ;; esac
if [ "${ttyd_ready}" != "1" ]; then
curl -fsSL -o "${tools}/bin/ttyd.tmp" https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.aarch64
printf '%s %s\n' b38acadd89d1d396a0f5649aa52c539edbad07f4bc7348b27b4f4b7219dd4165 "${tools}/bin/ttyd.tmp" | sha256sum -c -
chmod 0755 "${tools}/bin/ttyd.tmp"
mv "${tools}/bin/ttyd.tmp" "${tools}/bin/ttyd"
fi
if [ ! -f "${tools}/.cli-versions-0.147.0-2.1.226" ]; then if [ ! -f "${tools}/.cli-versions-0.147.0-2.1.226" ]; then
npm install --global --omit=dev --no-audit --no-fund --prefix "${tools}" \ npm install --global --omit=dev --no-audit --no-fund --prefix "${tools}" \
@openai/codex@0.147.0 \ @openai/codex@0.147.0 \
@ -332,6 +340,65 @@ spec:
resources: resources:
requests: {cpu: 50m, memory: 128Mi} requests: {cpu: 50m, memory: 128Mi}
limits: {cpu: 750m, memory: 1Gi} limits: {cpu: 750m, memory: 1Gi}
- name: herdr-tui
image: registry.bstein.dev/bstein/hermes-agent@sha256:15c5c538c0b58686af2e54e10bc870b23284789d485a609349df24ed3053622f
imagePullPolicy: IfNotPresent
command: [/bin/sh, -ec]
args:
- |
set -a
. /opt/data/.env
set +a
exec /opt/data/tools/bin/ttyd \
--writable \
--check-origin \
--auth-header X-Forwarded-User \
--interface 0.0.0.0 \
--port 7681 \
--cwd /opt/data/workspace \
--terminal-type xterm-256color \
--client-option "titleFixed=Hermes Agent - HERDR" \
--client-option fontSize=15 \
/opt/data/tools/bin/herdr
ports:
- {name: herdr-tui, containerPort: 7681, protocol: TCP}
env:
- {name: HOME, value: /opt/data/home}
- {name: CODEX_HOME, value: /opt/data/home/.codex}
- {name: CLAUDE_CONFIG_DIR, value: /opt/data/home/.claude}
- {name: HERDR_CONFIG_PATH, value: /opt/data/home/.config/herdr/config.toml}
- {name: HERDR_SOCKET_PATH, value: /opt/data/herdr/herdr.sock}
- {name: PATH, value: /opt/data/tools/bin:/usr/local/bin:/usr/bin:/bin}
volumeMounts:
- {name: home, mountPath: /opt/data}
- {name: tmp, mountPath: /tmp}
startupProbe:
tcpSocket: {port: herdr-tui}
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 60
readinessProbe:
tcpSocket: {port: herdr-tui}
periodSeconds: 10
timeoutSeconds: 3
livenessProbe:
tcpSocket: {port: herdr-tui}
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
resources:
requests: {cpu: 25m, memory: 64Mi}
limits: {cpu: 500m, memory: 512Mi}
- name: herdr-server - name: herdr-server
image: registry.bstein.dev/bstein/hermes-agent@sha256:15c5c538c0b58686af2e54e10bc870b23284789d485a609349df24ed3053622f image: registry.bstein.dev/bstein/hermes-agent@sha256:15c5c538c0b58686af2e54e10bc870b23284789d485a609349df24ed3053622f
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
@ -352,13 +419,37 @@ spec:
done done
herdr integration install codex || true herdr integration install codex || true
herdr integration install claude || true herdr integration install claude || true
pane_file=/opt/data/herdr/coordinator-pane-id
pane="$(cat "${pane_file}" 2>/dev/null || true)"
if [ -z "${pane}" ] || ! herdr pane get "${pane}" >/dev/null 2>&1; then
created="$(herdr workspace create \
--cwd /opt/data/workspace \
--label coordinator \
--env HERMES_HOME=/opt/data \
--env HERMES_AUTH_FILE=/shared-auth/auth.json \
--env HOME=/opt/data/home \
--env PYTHONPATH=/opt/hermes \
--focus)"
pane="$(printf '%s' "${created}" | /opt/hermes/.venv/bin/python -c \
'import json,sys; print(json.load(sys.stdin)["result"]["root_pane"]["pane_id"])')"
printf '%s\n' "${pane}" > "${pane_file}"
fi
process_info="$(herdr pane process-info --pane "${pane}" 2>/dev/null || printf '%s\n' '{"result":{"process_info":{"foreground_processes":[]}}}')"
coordinator_running="$(printf '%s' "${process_info}" | /opt/hermes/.venv/bin/python -c \
'import json,sys; processes=json.load(sys.stdin)["result"]["process_info"].get("foreground_processes", []); print("yes" if any("hermes" in " ".join(str(p.get(k) or "") for k in ("name", "argv0", "cmdline")) for p in processes) else "no")')"
if [ "${coordinator_running}" != "yes" ]; then
herdr pane run "${pane}" /opt/hermes/.venv/bin/hermes
fi
wait "${server_pid}" wait "${server_pid}"
env: env:
- {name: HERMES_HOME, value: /opt/data}
- {name: HERMES_AUTH_FILE, value: /shared-auth/auth.json}
- {name: HOME, value: /opt/data/home} - {name: HOME, value: /opt/data/home}
- {name: CODEX_HOME, value: /opt/data/home/.codex} - {name: CODEX_HOME, value: /opt/data/home/.codex}
- {name: CLAUDE_CONFIG_DIR, value: /opt/data/home/.claude} - {name: CLAUDE_CONFIG_DIR, value: /opt/data/home/.claude}
- {name: HERDR_CONFIG_PATH, value: /opt/data/home/.config/herdr/config.toml} - {name: HERDR_CONFIG_PATH, value: /opt/data/home/.config/herdr/config.toml}
- {name: HERDR_SOCKET_PATH, value: /opt/data/herdr/herdr.sock} - {name: HERDR_SOCKET_PATH, value: /opt/data/herdr/herdr.sock}
- {name: PYTHONPATH, value: /opt/hermes}
- {name: PATH, value: /opt/data/tools/bin:/usr/local/bin:/usr/bin:/bin} - {name: PATH, value: /opt/data/tools/bin:/usr/local/bin:/usr/bin:/bin}
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
@ -368,6 +459,10 @@ spec:
type: RuntimeDefault type: RuntimeDefault
volumeMounts: volumeMounts:
- {name: home, mountPath: /opt/data} - {name: home, mountPath: /opt/data}
- {name: provider-auth, mountPath: /shared-auth, readOnly: true}
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
- {name: coordinator, mountPath: /opt/data/home/.local/bin/herdr-dispatch, subPath: herdr_dispatch.py, readOnly: true}
resources: resources:
requests: {cpu: 50m, memory: 128Mi} requests: {cpu: 50m, memory: 128Mi}
limits: {cpu: "1", memory: 2Gi} limits: {cpu: "1", memory: 2Gi}

View File

@ -72,7 +72,7 @@ spec:
matchLabels: matchLabels:
app: oauth2-proxy-hermes-agent app: oauth2-proxy-hermes-agent
ports: ports:
- {protocol: TCP, port: 8787} - {protocol: TCP, port: 7681}
egress: egress:
- to: - to:
- namespaceSelector: - namespaceSelector:

View File

@ -96,7 +96,7 @@ spec:
- --cookie-samesite=lax - --cookie-samesite=lax
- --cookie-refresh=1h - --cookie-refresh=1h
- --cookie-expire=8h - --cookie-expire=8h
- --upstream=http://hermes-agent.hermes.svc.cluster.local:8787 - --upstream=http://hermes-agent.hermes.svc.cluster.local:7681
- --http-address=0.0.0.0:4180 - --http-address=0.0.0.0:4180
- --skip-provider-button=true - --skip-provider-button=true
- --reverse-proxy=true - --reverse-proxy=true

View File

@ -54,6 +54,9 @@ spec:
- name: dashboard - name: dashboard
port: 8787 port: 8787
targetPort: dashboard targetPort: dashboard
- name: herdr-tui
port: 7681
targetPort: herdr-tui
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service