diff --git a/services/hermes/agent-deployment.yaml b/services/hermes/agent-deployment.yaml index 5be29eec9..633e1e5c7 100644 --- a/services/hermes/agent-deployment.yaml +++ b/services/hermes/agent-deployment.yaml @@ -24,7 +24,7 @@ spec: ai.bstein.dev/execution: Herdr-supervised Codex and Claude Code 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/config-rev: "20260809-cassandra-readonly" + ai.bstein.dev/config-rev: "20260809-terminal-clipboard" vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: hermes-agent vault.hashicorp.com/agent-inject-secret-anthropic-token: kv/data/atlas/hermes/agent-tokens @@ -269,6 +269,27 @@ spec: resources: requests: {cpu: 25m, memory: 32Mi} limits: {cpu: 250m, memory: 128Mi} + - name: prepare-ttyd-index + image: registry.bstein.dev/bstein/hermes-agent@sha256:7a1daefae2f068dcf14e7eb1f2f9aab2e1ce79c1b55b4bb0aa1092fbf4019bbc + imagePullPolicy: IfNotPresent + command: + - /opt/hermes/.venv/bin/python + - /opt/coordinator/patch_ttyd_index.py + - /opt/data/tools/bin/ttyd + - /ttyd-index/index.html + securityContext: + allowPrivilegeEscalation: false + runAsUser: 10000 + runAsGroup: 10000 + seccompProfile: + type: RuntimeDefault + volumeMounts: + - {name: home, mountPath: /opt/data} + - {name: coordinator, mountPath: /opt/coordinator, readOnly: true} + - {name: ttyd-index, mountPath: /ttyd-index} + resources: + requests: {cpu: 25m, memory: 32Mi} + limits: {cpu: 250m, memory: 128Mi} containers: - name: hermes image: registry.bstein.dev/bstein/hermes-agent@sha256:7a1daefae2f068dcf14e7eb1f2f9aab2e1ce79c1b55b4bb0aa1092fbf4019bbc @@ -401,6 +422,7 @@ spec: --port 7681 \ --cwd /opt/data/workspace \ --terminal-type xterm-256color \ + --index /ttyd-index/index.html \ --client-option "titleFixed=Hermes Agent - HERDR" \ --client-option fontSize=15 \ /bin/sh -c ' @@ -423,6 +445,7 @@ spec: volumeMounts: - {name: home, mountPath: /opt/data} - {name: tmp, mountPath: /tmp} + - {name: ttyd-index, mountPath: /ttyd-index, readOnly: true} startupProbe: tcpSocket: {port: herdr-tui} periodSeconds: 5 @@ -603,3 +626,6 @@ spec: - name: tmp emptyDir: sizeLimit: 256Mi + - name: ttyd-index + emptyDir: + sizeLimit: 2Mi diff --git a/services/hermes/kustomization.yaml b/services/hermes/kustomization.yaml index 64740d87d..49251205c 100644 --- a/services/hermes/kustomization.yaml +++ b/services/hermes/kustomization.yaml @@ -50,6 +50,7 @@ configMapGenerator: - hermes_coordinator.py=scripts/hermes_coordinator.py - hermes_model_routing.py=scripts/hermes_model_routing.py - patch_hermes_auth.py=scripts/patch_hermes_auth.py + - patch_ttyd_index.py=scripts/patch_ttyd_index.py options: disableNameSuffixHash: true - name: hermes-agent-kubeconfig diff --git a/services/hermes/scripts/patch_ttyd_index.py b/services/hermes/scripts/patch_ttyd_index.py new file mode 100644 index 000000000..cb4a293c8 --- /dev/null +++ b/services/hermes/scripts/patch_ttyd_index.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +"""Add reliable browser clipboard handling to ttyd's pinned client page.""" + +from __future__ import annotations + +import argparse +import socket +import subprocess +import time +import urllib.request +from pathlib import Path + + +MARKER = "atlas-ttyd-clipboard" +UPSTREAM_COPY = 'document.execCommand("copy")' +CLIPBOARD_ADAPTER = r""" + + +""" + + +def patch_html(content: str) -> str: + """Insert the adapter once and disable ttyd's false-success copy call.""" + if content.count(UPSTREAM_COPY) != 1 or content.count("