hermes(agent): supervise stock dashboard with gateway
This commit is contained in:
parent
dfa12dd7f5
commit
22efb7d144
@ -24,7 +24,7 @@ spec:
|
|||||||
ai.bstein.dev/execution: Hermes Kanban with durable direct Codex and Claude Code CLI workers
|
ai.bstein.dev/execution: Hermes Kanban with durable direct Codex and Claude Code CLI workers
|
||||||
ai.bstein.dev/model-policy: Jetson-assisted AUTO routing, low through xhigh, cross-provider fallback
|
ai.bstein.dev/model-policy: Jetson-assisted AUTO routing, 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: "20260810-stock-dashboard-init-hardening"
|
ai.bstein.dev/config-rev: "20260811-stock-supervisor"
|
||||||
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
|
||||||
@ -319,10 +319,11 @@ spec:
|
|||||||
- name: hermes
|
- name: hermes
|
||||||
image: registry.bstein.dev/bstein/hermes-agent@sha256:46de11122226a68adad6d902e81ef9b4b53deb0433dcc61f57fa43fae2e3a3a5
|
image: registry.bstein.dev/bstein/hermes-agent@sha256:46de11122226a68adad6d902e81ef9b4b53deb0433dcc61f57fa43fae2e3a3a5
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
command: [/opt/hermes/.venv/bin/hermes]
|
command: [/init, /opt/hermes/docker/main-wrapper.sh]
|
||||||
args: [gateway, run, --no-supervise]
|
args: [gateway, run]
|
||||||
ports:
|
ports:
|
||||||
- {name: api, containerPort: 8642, protocol: TCP}
|
- {name: api, containerPort: 8642, protocol: TCP}
|
||||||
|
- {name: dashboard, containerPort: 9119, protocol: TCP}
|
||||||
env:
|
env:
|
||||||
- {name: HERMES_HOME, value: /opt/data}
|
- {name: HERMES_HOME, value: /opt/data}
|
||||||
- {name: HERMES_AUTH_FILE, value: /shared-auth/auth.json}
|
- {name: HERMES_AUTH_FILE, value: /shared-auth/auth.json}
|
||||||
@ -334,8 +335,12 @@ spec:
|
|||||||
- {name: PLAYWRIGHT_BROWSERS_PATH, value: /opt/hermes/.playwright}
|
- {name: PLAYWRIGHT_BROWSERS_PATH, value: /opt/hermes/.playwright}
|
||||||
- {name: AGENT_BROWSER_EXECUTABLE_PATH, value: /opt/hermes/.playwright/chromium_headless_shell-1228/chrome-linux/headless_shell}
|
- {name: AGENT_BROWSER_EXECUTABLE_PATH, value: /opt/hermes/.playwright/chromium_headless_shell-1228/chrome-linux/headless_shell}
|
||||||
- {name: AGENT_BROWSER_ARGS, value: "--no-sandbox,--disable-dev-shm-usage"}
|
- {name: AGENT_BROWSER_ARGS, value: "--no-sandbox,--disable-dev-shm-usage"}
|
||||||
- {name: HERMES_DASHBOARD, value: "0"}
|
- {name: HERMES_DASHBOARD, value: "1"}
|
||||||
|
- {name: HERMES_DASHBOARD_HOST, value: 127.0.0.1}
|
||||||
|
- {name: HERMES_DASHBOARD_PORT, value: "9119"}
|
||||||
|
- {name: HERMES_DASHBOARD_FILES_ROOT, value: /opt/data/workspace}
|
||||||
- {name: HERMES_DASHBOARD_PUBLIC_URL, value: https://agent.hermes.bstein.dev}
|
- {name: HERMES_DASHBOARD_PUBLIC_URL, value: https://agent.hermes.bstein.dev}
|
||||||
|
- {name: HERMES_TUI_AGENT_INIT_TIMEOUT_S, value: "180"}
|
||||||
- {name: API_SERVER_ENABLED, value: "true"}
|
- {name: API_SERVER_ENABLED, value: "true"}
|
||||||
- {name: API_SERVER_HOST, value: 0.0.0.0}
|
- {name: API_SERVER_HOST, value: 0.0.0.0}
|
||||||
- {name: API_SERVER_PORT, value: "8642"}
|
- {name: API_SERVER_PORT, value: "8642"}
|
||||||
@ -347,98 +352,34 @@ spec:
|
|||||||
- {name: provider-auth, mountPath: /shared-auth}
|
- {name: provider-auth, mountPath: /shared-auth}
|
||||||
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
|
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
|
||||||
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
||||||
|
- {name: tui-gateway-patch, mountPath: /opt/hermes/tui_gateway/server.py, subPath: server.py}
|
||||||
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly: true}
|
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly: true}
|
||||||
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true}
|
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true}
|
||||||
|
- {name: tmp, mountPath: /tmp}
|
||||||
startupProbe:
|
startupProbe:
|
||||||
tcpSocket: {port: api}
|
tcpSocket: {port: dashboard}
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
timeoutSeconds: 5
|
timeoutSeconds: 5
|
||||||
failureThreshold: 60
|
failureThreshold: 60
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
tcpSocket: {port: api}
|
tcpSocket: {port: dashboard}
|
||||||
initialDelaySeconds: 30
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
timeoutSeconds: 5
|
timeoutSeconds: 5
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
tcpSocket: {port: api}
|
tcpSocket: {port: dashboard}
|
||||||
initialDelaySeconds: 90
|
initialDelaySeconds: 90
|
||||||
periodSeconds: 30
|
periodSeconds: 30
|
||||||
timeoutSeconds: 10
|
timeoutSeconds: 10
|
||||||
securityContext:
|
securityContext:
|
||||||
allowPrivilegeEscalation: false
|
allowPrivilegeEscalation: false
|
||||||
runAsUser: 10000
|
runAsUser: 0
|
||||||
runAsGroup: 10000
|
runAsGroup: 0
|
||||||
seccompProfile:
|
seccompProfile:
|
||||||
type: RuntimeDefault
|
type: RuntimeDefault
|
||||||
resources:
|
resources:
|
||||||
requests: {cpu: 250m, memory: 512Mi}
|
requests: {cpu: 350m, memory: 768Mi}
|
||||||
limits: {cpu: "2", memory: 4Gi}
|
limits: {cpu: "3", memory: 6Gi}
|
||||||
- name: dashboard
|
|
||||||
image: registry.bstein.dev/bstein/hermes-agent@sha256:46de11122226a68adad6d902e81ef9b4b53deb0433dcc61f57fa43fae2e3a3a5
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
command: [/bin/sh, -ec]
|
|
||||||
args:
|
|
||||||
- |
|
|
||||||
set -a
|
|
||||||
. /opt/data/.env
|
|
||||||
set +a
|
|
||||||
exec /opt/hermes/.venv/bin/hermes dashboard \
|
|
||||||
--host 127.0.0.1 \
|
|
||||||
--port 9119 \
|
|
||||||
--skip-build \
|
|
||||||
--no-open
|
|
||||||
ports:
|
|
||||||
- {name: dashboard, containerPort: 9119, protocol: TCP}
|
|
||||||
env:
|
|
||||||
- {name: HERMES_HOME, value: /opt/data}
|
|
||||||
- {name: HERMES_AUTH_FILE, value: /shared-auth/auth.json}
|
|
||||||
- {name: HOME, value: /opt/data/home}
|
|
||||||
- {name: CODEX_HOME, value: /opt/data/home/.codex}
|
|
||||||
- {name: CLAUDE_CONFIG_DIR, value: /opt/data/home/.claude}
|
|
||||||
- {name: KUBECONFIG, value: /opt/data/home/.kube/config}
|
|
||||||
- {name: PYTHONPATH, value: /opt/hermes}
|
|
||||||
- {name: PATH, value: /opt/coordinator:/opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin}
|
|
||||||
- {name: PLAYWRIGHT_BROWSERS_PATH, value: /opt/hermes/.playwright}
|
|
||||||
- {name: AGENT_BROWSER_EXECUTABLE_PATH, value: /opt/hermes/.playwright/chromium_headless_shell-1228/chrome-linux/headless_shell}
|
|
||||||
- {name: AGENT_BROWSER_ARGS, value: "--no-sandbox,--disable-dev-shm-usage"}
|
|
||||||
- {name: HERMES_DASHBOARD_FILES_ROOT, value: /opt/data/workspace}
|
|
||||||
- {name: HERMES_DASHBOARD_PUBLIC_URL, value: https://agent.hermes.bstein.dev}
|
|
||||||
- {name: HERMES_TUI_AGENT_INIT_TIMEOUT_S, value: "180"}
|
|
||||||
volumeMounts:
|
|
||||||
- {name: home, mountPath: /opt/data}
|
|
||||||
- {name: provider-auth, mountPath: /shared-auth}
|
|
||||||
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
|
|
||||||
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
|
||||||
- {name: tui-gateway-patch, mountPath: /opt/hermes/tui_gateway/server.py, subPath: server.py}
|
|
||||||
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly: true}
|
|
||||||
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true}
|
|
||||||
- {name: tmp, mountPath: /tmp}
|
|
||||||
workingDir: /opt/data/workspace
|
|
||||||
readinessProbe:
|
|
||||||
exec:
|
|
||||||
command: [curl, -fsS, http://127.0.0.1:9119/api/status]
|
|
||||||
initialDelaySeconds: 20
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 5
|
|
||||||
livenessProbe:
|
|
||||||
exec:
|
|
||||||
command: [curl, -fsS, http://127.0.0.1:9119/api/status]
|
|
||||||
initialDelaySeconds: 60
|
|
||||||
periodSeconds: 30
|
|
||||||
timeoutSeconds: 10
|
|
||||||
securityContext:
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
capabilities:
|
|
||||||
drop: [ALL]
|
|
||||||
readOnlyRootFilesystem: true
|
|
||||||
runAsNonRoot: true
|
|
||||||
runAsUser: 10000
|
|
||||||
runAsGroup: 10000
|
|
||||||
seccompProfile:
|
|
||||||
type: RuntimeDefault
|
|
||||||
resources:
|
|
||||||
requests: {cpu: 100m, memory: 256Mi}
|
|
||||||
limits: {cpu: "2", memory: 4Gi}
|
|
||||||
- name: oauth2-proxy
|
- name: oauth2-proxy
|
||||||
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
|
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
|
|||||||
@ -632,15 +632,17 @@ def test_agent_root_is_stock_dashboard_and_terminal_is_a_separate_path():
|
|||||||
containers = {item["name"]: item for item in pod["containers"]}
|
containers = {item["name"]: item for item in pod["containers"]}
|
||||||
assert "webui" not in containers
|
assert "webui" not in containers
|
||||||
|
|
||||||
dashboard = containers["dashboard"]
|
assert "dashboard" not in containers
|
||||||
dashboard_command = dashboard["args"][0]
|
hermes = containers["hermes"]
|
||||||
assert "/opt/hermes/.venv/bin/hermes dashboard" in dashboard_command
|
assert hermes["command"] == ["/init", "/opt/hermes/docker/main-wrapper.sh"]
|
||||||
assert "--host 127.0.0.1" in dashboard_command
|
assert hermes["args"] == ["gateway", "run"]
|
||||||
assert "--port 9119" in dashboard_command
|
hermes_env = {item["name"]: item["value"] for item in hermes["env"]}
|
||||||
assert "--skip-build" in dashboard_command
|
assert hermes_env["HERMES_DASHBOARD"] == "1"
|
||||||
assert "--no-open" in dashboard_command
|
assert hermes_env["HERMES_DASHBOARD_HOST"] == "127.0.0.1"
|
||||||
dashboard_env = {item["name"]: item["value"] for item in dashboard["env"]}
|
assert hermes_env["HERMES_DASHBOARD_PORT"] == "9119"
|
||||||
assert dashboard_env["HERMES_TUI_AGENT_INIT_TIMEOUT_S"] == "180"
|
assert hermes_env["HERMES_TUI_AGENT_INIT_TIMEOUT_S"] == "180"
|
||||||
|
assert hermes["securityContext"]["runAsUser"] == 0
|
||||||
|
assert hermes["securityContext"]["runAsGroup"] == 0
|
||||||
|
|
||||||
terminal = containers["terminal"]
|
terminal = containers["terminal"]
|
||||||
command = terminal["args"][0]
|
command = terminal["args"][0]
|
||||||
@ -665,7 +667,7 @@ def test_agent_root_is_stock_dashboard_and_terminal_is_a_separate_path():
|
|||||||
if item["name"] == "patch-tui-gateway"
|
if item["name"] == "patch-tui-gateway"
|
||||||
)
|
)
|
||||||
assert patch_init["command"][-1] == "/patched/server.py"
|
assert patch_init["command"][-1] == "/patched/server.py"
|
||||||
for name in ("dashboard", "terminal"):
|
for name in ("hermes", "terminal"):
|
||||||
mounts = containers[name]["volumeMounts"]
|
mounts = containers[name]["volumeMounts"]
|
||||||
assert {
|
assert {
|
||||||
"name": "tui-gateway-patch",
|
"name": "tui-gateway-patch",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user