Rovatiana commited on
Commit
89f49e8
Β·
1 Parent(s): cb1a6b0

Deploy Hermes Workspace + Agent single container

Browse files

- Dockerfile: Multi-stage build (Python agent + Node.js workspace)
- start.sh: Process supervision (agent β†’ health-check β†’ workspace)
- app.yaml: HF Spaces Docker SDK configuration
- .dockerignore: Optimized build context

Architecture: Single container with Hermes Agent (port 8642) and
Hermes Workspace (port 7860) communicating via localhost.

Files changed (4) hide show
  1. .dockerignore +103 -0
  2. Dockerfile +106 -0
  3. app.yaml +14 -0
  4. start.sh +132 -0
.dockerignore ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =============================================================================
2
+ # HuggingMes β€” Docker Build Context Filter
3
+ # =============================================================================
4
+ # Excludes files that are not needed inside the HF Spaces container,
5
+ # reducing build context size and speeding up uploads.
6
+ # =============================================================================
7
+
8
+ # Build artefacts (regenerated inside container)
9
+ node_modules
10
+ dist
11
+ .next
12
+ .venv
13
+ venv
14
+ __pycache__
15
+ *.pyc
16
+ *.pyo
17
+
18
+ # Version control
19
+ .git
20
+ .gitignore
21
+ .gitattributes
22
+
23
+ # IDE / editor
24
+ .vscode
25
+ .idea
26
+ *.swp
27
+ *.swo
28
+ *~
29
+
30
+ # Environment / secrets (must be set via HF Spaces secrets, never baked in)
31
+ .env
32
+ .env.local
33
+ .env.*.local
34
+ .envrc
35
+
36
+ # Documentation (not needed at runtime)
37
+ docs/
38
+ *.md
39
+ CHANGELOG*
40
+ LICENSE
41
+ SECURITY*
42
+ CONTRIBUTING*
43
+ FEATURES*
44
+ FUTURE*
45
+ screenshots/
46
+ infographic/
47
+
48
+ # Dev tooling config
49
+ eslint.config.js
50
+ prettier.config.js
51
+ tsconfig.json
52
+ vite.config.ts
53
+ wrangler.jsonc
54
+ flake.lock
55
+ flake.nix
56
+ nix/
57
+
58
+ # CI / GitHub
59
+ .github/
60
+
61
+ # Dev containers
62
+ .devcontainer/
63
+ docker-compose*.yml
64
+ docker/
65
+
66
+ # Tests
67
+ e2e/
68
+ tests/
69
+ playground-ws-worker/
70
+
71
+ # Nix / packaging
72
+ nix/
73
+ packaging/
74
+ setup.py
75
+ MANIFEST.in
76
+
77
+ # OS artefacts
78
+ .DS_Store
79
+ Thumbs.db
80
+
81
+ # Large assets not needed at runtime
82
+ assets/
83
+ plans/
84
+ *.plans/
85
+
86
+ # Hermes Agent source (installed via pip, not copied)
87
+ agent/
88
+ gateway/
89
+ hermes_cli/
90
+ hermes*.py
91
+ acp_adapter/
92
+ cron/
93
+ plugins/
94
+ providers/
95
+ tools/
96
+ toolsets.py
97
+ skills/
98
+ uv.lock
99
+
100
+ # This Dockerfile is for reference only; Dockerfile.hf is used for builds
101
+ Dockerfile.hf
102
+ start.sh
103
+ app.yaml
Dockerfile ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # syntax=docker/dockerfile:1.6
2
+ # =============================================================================
3
+ # HuggingFace Spaces β€” Single-Container Deployment
4
+ # =============================================================================
5
+ # Multi-stage build bundling Hermes Agent (Python) + Hermes Workspace (Node.js)
6
+ # into one container for HF Spaces (free tier: 2 vCPU / 16 GB).
7
+ #
8
+ # Stage 1: Install hermes-agent + dependencies (Python 3.12)
9
+ # Stage 2: Build Hermes Workspace (Node.js 22 via nodesource)
10
+ # Stage 3: Runtime β€” both installed, supervised by start.sh
11
+ #
12
+ # Build:
13
+ # docker build -f Dockerfile.hf -t huggingmes .
14
+ #
15
+ # HF Spaces auto-builds from this file when present in the repo root or
16
+ # specified via the SDK configuration.
17
+ # =============================================================================
18
+
19
+ # ─── Stage 1: Python β€” install hermes-agent ────────────────────────────────
20
+ FROM python:3.12-slim-bookworm AS agent-install
21
+
22
+ # Install build essentials (needed for some pip wheels)
23
+ RUN apt-get update && apt-get install -y --no-install-recommends \
24
+ build-essential curl ca-certificates \
25
+ && rm -rf /var/lib/apt/lists/*
26
+
27
+ # Install hermes-agent with the "web" extras for the API server (fastapi/uvicorn/aiohttp)
28
+ RUN pip install --no-cache-dir --prefix=/install hermes-agent[web]
29
+
30
+ # ─── Stage 2: Node.js β€” build Hermes Workspace ─────────────────────────────
31
+ FROM node:22-slim AS workspace-build
32
+
33
+ RUN corepack enable && \
34
+ apt-get update && \
35
+ apt-get install -y --no-install-recommends ca-certificates && \
36
+ rm -rf /var/lib/apt/lists/*
37
+
38
+ WORKDIR /app
39
+
40
+ # Install deps (cache-friendly: copy only manifests first)
41
+ COPY package.json pnpm-lock.yaml* ./
42
+ RUN pnpm install --frozen-lockfile
43
+
44
+ # Copy sources and build
45
+ COPY . .
46
+ RUN pnpm build
47
+
48
+ # ─── Stage 3: Runtime β€” unified container ──────────────────────────────────
49
+ FROM python:3.12-slim-bookworm
50
+
51
+ # --- Install Node.js 22 via nodesource ---
52
+ RUN apt-get update && apt-get install -y --no-install-recommends \
53
+ ca-certificates curl gnupg tini python3 \
54
+ && mkdir -p /etc/apt/keyrings \
55
+ && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
56
+ | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
57
+ && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" \
58
+ > /etc/apt/sources.list.d/nodesource.list \
59
+ && apt-get update \
60
+ && apt-get install -y --no-install-recommends nodejs \
61
+ && rm -rf /var/lib/apt/lists/* \
62
+ && corepack enable \
63
+ && groupadd -r workspace && useradd -r -g workspace -u 1001 -m workspace
64
+
65
+ # --- Copy Python (hermes-agent) from Stage 1 ---
66
+ COPY --from=agent-install /install /usr/local
67
+
68
+ # --- Copy workspace build artefacts from Stage 2 ---
69
+ WORKDIR /app
70
+ COPY --from=workspace-build --chown=workspace:workspace /app/dist ./dist
71
+ COPY --from=workspace-build --chown=workspace:workspace /app/node_modules ./node_modules
72
+ COPY --from=workspace-build --chown=workspace:workspace /app/package.json ./package.json
73
+ COPY --from=workspace-build --chown=workspace:workspace /app/server-entry.js ./server-entry.js
74
+ COPY --from=workspace-build --chown=workspace:workspace /app/skills ./skills
75
+
76
+ # --- Copy startup script ---
77
+ COPY --chown=workspace:workspace start.sh /usr/local/bin/start.sh
78
+ RUN chmod +x /usr/local/bin/start.sh
79
+
80
+ # --- Create hermes config dir for the agent ---
81
+ RUN mkdir -p /home/workspace/.hermes && chown -R workspace:workspace /home/workspace/.hermes
82
+
83
+ # --- Environment variables ---
84
+ # HF Spaces sets $PORT automatically; we default to 7860 (the workspace port)
85
+ # The agent API always runs on 8642 internally
86
+ ENV NODE_ENV=production \
87
+ HERMES_API_URL=http://localhost:8642 \
88
+ PORT=7860 \
89
+ HOST=0.0.0.0 \
90
+ API_SERVER_ENABLED=true \
91
+ API_SERVER_HOST=127.0.0.1 \
92
+ API_SERVER_PORT=8642 \
93
+ PYTHONUNBUFFERED=1
94
+
95
+ # HF Spaces default port
96
+ EXPOSE 7860
97
+
98
+ # Health check: verify the workspace HTTP server is responding
99
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
100
+ CMD curl -fsS http://127.0.0.1:7860/ >/dev/null || exit 1
101
+
102
+ # Run as non-root user
103
+ USER workspace
104
+
105
+ # Entrypoint: start agent β†’ health-check β†’ start workspace β†’ supervise
106
+ ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/start.sh"]
app.yaml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ title: HuggingMes
2
+ emoji: πŸ€–
3
+ colorFrom: indigo
4
+ colorTo: purple
5
+ sdk: docker
6
+ pinned: false
7
+ license: mit
8
+ short_description: Hermes Workspace β€” AI agent chat and multi-agent coding pipelines
9
+ tags:
10
+ - agent
11
+ - ai
12
+ - coding
13
+ - workspace
14
+ - hermes
start.sh ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # =============================================================================
3
+ # HuggingMes β€” Startup Script for HF Spaces
4
+ # =============================================================================
5
+ # Sequence:
6
+ # 1. Launch Hermes Agent gateway with API server enabled (port 8642)
7
+ # 2. Health-check the agent API endpoint
8
+ # 3. Launch Hermes Workspace HTTP server (port $PORT, default 7860)
9
+ # 4. Supervise both processes β€” restart workspace if it crashes;
10
+ # exit if the agent dies (it's the core dependency)
11
+ # =============================================================================
12
+ set -euo pipefail
13
+
14
+ # --- Configuration ---
15
+ AGENT_PORT="${API_SERVER_PORT:-8642}"
16
+ WORKSPACE_PORT="${PORT:-7860}"
17
+ WORKSPACE_HOST="${HOST:-0.0.0.0}"
18
+ AGENT_HEALTH_URL="http://127.0.0.1:${AGENT_PORT}/health"
19
+ HEALTH_RETRIES=30
20
+ HEALTH_INTERVAL=2
21
+
22
+ # --- Color output helpers ---
23
+ log() { echo "[$(date -u +%H:%M:%S)] $*"; }
24
+ ok() { log "βœ… $*"; }
25
+ fail() { log "❌ $*"; }
26
+ info() { log "ℹ️ $*"; }
27
+
28
+ # =============================================================================
29
+ # Step 1: Configure and launch Hermes Agent gateway
30
+ # =============================================================================
31
+ log "─────────────────────────────────────────────"
32
+ log "Starting Hermes Agent (API server on :${AGENT_PORT})..."
33
+ log "─────────────────────────────────────────────"
34
+
35
+ # Ensure API server is enabled in the agent config
36
+ export API_SERVER_ENABLED=true
37
+ export API_SERVER_HOST=127.0.0.1
38
+ export API_SERVER_PORT="${AGENT_PORT}"
39
+
40
+ # Launch the agent gateway in background
41
+ # The gateway will start the api_server platform on port 8642 because
42
+ # API_SERVER_ENABLED=true is set
43
+ hermes gateway run --quiet &
44
+ AGENT_PID=$!
45
+ log "Agent PID: ${AGENT_PID}"
46
+
47
+ # =============================================================================
48
+ # Step 2: Health-check the agent API
49
+ # =============================================================================
50
+ log "Waiting for agent API to become healthy..."
51
+
52
+ attempt=0
53
+ while [ $attempt -lt $HEALTH_RETRIES ]; do
54
+ attempt=$((attempt + 1))
55
+ if curl -fsS "${AGENT_HEALTH_URL}" >/dev/null 2>&1; then
56
+ ok "Agent API is healthy (attempt ${attempt})"
57
+ break
58
+ fi
59
+ # Check if agent process is still alive
60
+ if ! kill -0 "$AGENT_PID" 2>/dev/null; then
61
+ fail "Agent process died during startup (PID ${AGENT_PID})"
62
+ exit 1
63
+ fi
64
+ info "Agent not ready yet (attempt ${attempt}/${HEALTH_RETRIES})..."
65
+ sleep "$HEALTH_INTERVAL"
66
+ done
67
+
68
+ if [ $attempt -ge $HEALTH_RETRIES ]; then
69
+ fail "Agent API failed to become healthy after $((HEALTH_RETRIES * HEALTH_INTERVAL))s"
70
+ # Print agent logs for debugging
71
+ kill -0 "$AGENT_PID" 2>/dev/null && kill "$AGENT_PID" 2>/dev/null || true
72
+ exit 1
73
+ fi
74
+
75
+ # =============================================================================
76
+ # Step 3: Launch Hermes Workspace HTTP server
77
+ # =============================================================================
78
+ log "─────────────────────────────────────────────"
79
+ log "Starting Hermes Workspace (port :${WORKSPACE_PORT})..."
80
+ log "─────────────────────────────────────────────"
81
+
82
+ # Workspace needs these env vars
83
+ export NODE_ENV=production
84
+ export HERMES_API_URL="http://localhost:${AGENT_PORT}"
85
+ export PORT="${WORKSPACE_PORT}"
86
+ export HOST="${WORKSPACE_HOST}"
87
+
88
+ # Launch workspace in background
89
+ node --max-old-space-size=2048 /app/server-entry.js &
90
+ WORKSPACE_PID=$!
91
+ log "Workspace PID: ${WORKSPACE_PID}"
92
+
93
+ # =============================================================================
94
+ # Step 4: Supervise both processes
95
+ # =============================================================================
96
+ log "─────────────────────────────────────────────"
97
+ log "Supervising: Agent(${AGENT_PID}) + Workspace(${WORKSPACE_PID})"
98
+ log "─────────────────────────────────────────────"
99
+
100
+ # Trap signals for clean shutdown
101
+ cleanup() {
102
+ log "Shutting down..."
103
+ kill "$WORKSPACE_PID" 2>/dev/null || true
104
+ kill "$AGENT_PID" 2>/dev/null || true
105
+ wait "$WORKSPACE_PID" 2>/dev/null || true
106
+ wait "$AGENT_PID" 2>/dev/null || true
107
+ log "All processes stopped."
108
+ exit 0
109
+ }
110
+ trap cleanup SIGTERM SIGINT SIGHUP
111
+
112
+ # Main supervision loop
113
+ while true; do
114
+ # Check if agent is still alive
115
+ if ! kill -0 "$AGENT_PID" 2>/dev/null; then
116
+ wait "$AGENT_PID" 2>/dev/null || true
117
+ fail "Agent process exited. Shutting down workspace."
118
+ kill "$WORKSPACE_PID" 2>/dev/null || true
119
+ exit 1
120
+ fi
121
+
122
+ # Check if workspace is still alive
123
+ if ! kill -0 "$WORKSPACE_PID" 2>/dev/null; then
124
+ wait "$WORKSPACE_PID" 2>/dev/null || true
125
+ log "⚠️ Workspace process exited. Restarting..."
126
+ node --max-old-space-size=2048 /app/server-entry.js &
127
+ WORKSPACE_PID=$!
128
+ log "Workspace restarted (PID: ${WORKSPACE_PID})"
129
+ fi
130
+
131
+ sleep 5
132
+ done