Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
4c575a1
Add Dockerfile and entrypoint script for devcontainer setup; update C…
gitricko Jul 21, 2026
c27f029
fix: add zstd to Dockerfile apt packages (required by Ollama installer)
gitricko Jul 21, 2026
271b06a
fix: add OLLAMA_NO_START=1 env and fix self-check.sh array syntax
gitricko Jul 21, 2026
b6d0a69
fix: escape log path in start_service and bypass entrypoint in CI smo…
gitricko Jul 21, 2026
86621ba
fix: make tailscale install non-fatal and optional in CI smoke test
gitricko Jul 21, 2026
2b34cb2
ci: share Docker image via GHCR temp tags, eliminate redundant rebuilds
gitricko Jul 21, 2026
84557b8
ci: fix cleanup job - URL-encode package name for GHCR API
gitricko Jul 21, 2026
13f2a57
ci: integration test runs entrypoint.sh to start services before self…
gitricko Jul 21, 2026
3fe7c28
fix: reduce image size from 9.2GB by combining RUN layers + add .dock…
gitricko Jul 21, 2026
a5fc9c7
fix: correct package name URL-encoding for GHCR API in cleanup job
gitricko Jul 21, 2026
a09fe77
dev
gitricko Jul 21, 2026
91d44d0
fix(ci): replace user/packages API with OCI Distribution API for GHCR…
gitricko Jul 21, 2026
5103064
fix(ci): use classic PAT for GHCR cleanup instead of GITHUB_TOKEN
gitricko Jul 21, 2026
edd7973
fix(ci): strip username prefix from package name in GHCR cleanup
gitricko Jul 21, 2026
1b93270
Dockerization 2
gitricko Jul 22, 2026
5e9beb3
fix(dockerfile): ensurepip + non-fatal ACP install so npm chain never…
gitricko Jul 22, 2026
3b7e143
feat(devcontainer): switch to pre-built GHCR image for Codespaces
gitricko Jul 22, 2026
5527622
fix(ci): fix metadata-action enable expressions for PR tags
gitricko Jul 22, 2026
15fcc05
fix(ci): cleanup job skips versions with persistent tags
gitricko Jul 22, 2026
e1797b6
fix(start-hermes): FHS venv detection + mkdir for USER.md
gitricko Jul 22, 2026
f852f90
fix(devcontainer): ensure entrypoint runs + skip dashboard build
gitricko Jul 22, 2026
97ccc40
fix: move config files out of /tmp to avoid Codespaces bind mount wipe
gitricko Jul 22, 2026
1d5c422
Update Dockerfile
gitricko Jul 22, 2026
cc59c01
Adjust wait time for service startup
gitricko Jul 22, 2026
4d06685
Update user identification command in CI workflow
gitricko Jul 22, 2026
92ac255
Change base image to universal for Dockerfile
gitricko Jul 22, 2026
0ba4967
dev
gitricko Jul 25, 2026
c76e354
fix: create vscode user (UID 1000) in universal base image for non-ro…
gitricko Jul 25, 2026
96a0283
fix: create vscode user only if missing (universal base may already h…
gitricko Jul 25, 2026
b444459
fix: handle vscode user creation properly in universal base image
gitricko Jul 25, 2026
48acbf9
fix: simply add sudo for existing vscode user in universal base image
gitricko Jul 25, 2026
b0656eb
fix: add sudo for vscode user in main build layer (not separate layer)
gitricko Jul 25, 2026
ad5d3bf
fix: actually create vscode user (groupadd/useradd) — universal base …
gitricko Jul 26, 2026
43bc98f
fix: check if GID 1000/vscode user exist before creating — base image…
gitricko Jul 26, 2026
ed3818f
fix: bust GHA build cache (scope=vscode-user-v2) to force fresh layer…
gitricko Jul 26, 2026
6e69782
fix: use UID 1000 instead of hardcoded 'vscode' username
gitricko Jul 26, 2026
efd7094
fix: replace broken 'bash echo $USER' with 'whoami' in CI
gitricko Jul 26, 2026
84309d7
fix: initialize Hermes config when config.yaml missing (not sessions)
gitricko Jul 26, 2026
446c1f0
dev
gitricko Jul 26, 2026
a0694f7
failure
gitricko Jul 27, 2026
e74d55f
fix: build Hermes web UI in Dockerfile for --skip-build
gitricko Jul 27, 2026
8f03c36
reduce size part 1
gitricko Jul 28, 2026
a3f4d8a
fix: Dockerfile parse error - ensure if/elif/else/fi block chains pro…
gitricko Jul 28, 2026
0767fad
chore: reduce image size — switch to base:ubuntu, remove cline + Node…
gitricko Jul 28, 2026
3bd7253
fix: add python3, nodejs, npm to apt-get (base:ubuntu lacks them)
gitricko Jul 28, 2026
f7c1d59
fix: disable ollama auto-pull to reduce RAM pressure on fresh start
gitricko Jul 28, 2026
fdb1ec9
3min then pull ollama
gitricko Jul 29, 2026
bbeb327
Bake ollama nomic-embed-text into Docker image
Jul 29, 2026
61a83ff
Add Ollama integration tests (smoke + self-check)
Jul 29, 2026
a4d5f20
Fix Ollama model path for integration test
Jul 29, 2026
9010344
Fix Ollama model path in self-check.sh and CI
Jul 29, 2026
5fc726c
Fix Ollama model dir permissions - mkdir + chmod before pull
Jul 29, 2026
b921098
Fix Ollama model dir - recursive chmod on .ollama parent
Jul 29, 2026
943d3b3
Fix Ollama model dir permissions (777 + a+rwX); remove Ollama check f…
Jul 29, 2026
3cd61f2
Fix: Set OLLAMA_MODELS in entrypoint.sh to baked model path
Jul 29, 2026
449cec7
Fix: Ensure Ollama model dir exists and is writable by vscode user
Jul 29, 2026
802f08e
Fix: Ensure Ollama model dir and all parent dirs writable by vscode user
Jul 29, 2026
e320b19
Fix: Ensure Ollama model dir permissions (a+rwX) during model bake layer
Jul 29, 2026
fc3280d
Fix: Ensure /usr/share/ollama is traversable by vscode user (chmod a+rx)
Jul 29, 2026
ff34189
Fix: Set OLLAMA_MODELS in ollama serve during model bake so model lan…
Jul 29, 2026
4b317dd
Fix: Add model file verification after ollama pull during bake
Jul 29, 2026
9bafc72
fix: prevent set -e crash on grep -c returning exit 1
Jul 29, 2026
890cf99
fix: tolerate Ollama model warnings in CI integration test
Jul 29, 2026
da0e8b8
fix: add retry loop + filesystem fallback for Ollama model/embedding …
Jul 30, 2026
b9a7a7d
fix: remove fragile ollama serve background process during model pull
Jul 30, 2026
db707f4
fix: restore && before ollama pull (comment broke chain)
Jul 30, 2026
b6d642d
fix: remove dangling && from ollama list (broke RUN chain)
Jul 30, 2026
53a368b
fix: add missing continuation bridge between ollama and hermes sections
Jul 30, 2026
0f6e3e3
fix: add 'true' chain terminator matching original pattern
Jul 30, 2026
5cd909f
fix: restore ollama serve for pull (client needs running server)
Jul 30, 2026
9eaf46f
fix: use curl health check for ollama server readiness (not ollama list)
Jul 30, 2026
4c9c6fb
fix: increase ollama health check wait to 5s and show curl errors
Jul 30, 2026
fad0403
Update versions for Hermes, OmniRoute, and Ollama
gitricko Jul 30, 2026
2483e66
Change installation commands from sh to bash
gitricko Jul 30, 2026
618bc79
Update Ollama installation method in Dockerfile
gitricko Jul 30, 2026
93a5d2c
Update download method for Ollama in Dockerfile
gitricko Jul 30, 2026
f7e02cb
Use official Ollama image for binary installation
gitricko Jul 30, 2026
231e7cb
Move environment variable exports to new location
gitricko Jul 30, 2026
227138a
Manul Merge - Lao
gitricko Aug 1, 2026
65b0e03
dev
gitricko Aug 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 249 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
# ── Baked Devcontainer Image ──────────────────────────────────────────
# Pre-installs all heavy tooling so containers start in seconds, not minutes.
# Rebuild: docker build -t hermes-codespace:latest -f .devcontainer/Dockerfile .
# ────────────────────────────────────────────────────────────────────────

# ── Ollama version (only needed for the FROM instruction) ───────────────
ARG OLLAMA_VERSION=0.32.5

# Use the official Ollama image to get the binary
FROM ollama/ollama:${OLLAMA_VERSION} AS ollama-bin

# ── Node.js builder: ModelRelay + OmniRoute + Hermes Web UI + TUI ───────
FROM node:24-slim AS node-builder

ARG OMNIROUTE_VERSION
ARG HERMES_VERSION

# Install git for GitHub-based npm installs and configure HTTPS
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& git config --global url."https://github.com/".insteadOf "ssh://git@github.com/"

# ── ModelRelay ────────────────────────────────────────────────────────
RUN npm install github:gitricko/modelrelay -g --prefix /build/modelrelay

# ── OmniRoute ─────────────────────────────────────────────────────────
RUN npm install omniroute@${OMNIROUTE_VERSION} -g --prefix /build/omniroute

# ── OmniRoute dist/ dep repair (hollow deps workaround) ──────────────
RUN omni_root="/build/omniroute/lib/node_modules/omniroute" \
&& dist_nm="$omni_root/dist/node_modules" \
&& parent_nm="$omni_root/node_modules" \
&& if [ -d "$dist_nm" ]; then \
for dst in $(find "$dist_nm" -mindepth 1 -maxdepth 1 -type d 2>/dev/null); do \
rel="${dst#"$dist_nm"/}" \
&& src="$parent_nm/$rel" \
&& if [ -d "$src" ] && [ ! "$(find "$dst" \( -name '*.js' -o -name '*.mjs' -o -name '*.node' \) -type f 2>/dev/null | head -1)" ] \
&& [ "$(find "$src" \( -name '*.js' -o -name '*.mjs' -o -name '*.node' \) -type f 2>/dev/null | head -1)" ]; then \
rm -rf "$dst" && cp -r "$src" "$dst" \
&& echo "Repaired hollow dep: $rel"; \
fi; \
done; \
fi

# ── Hermes Web UI ─────────────────────────────────────────────────────
# Build output lands at hermes_cli/web_dist relative to the repo root,
# NOT relative to the web/ directory. So from web/ it's ../hermes_cli/web_dist
RUN git clone --depth 1 --branch ${HERMES_VERSION} https://github.com/NousResearch/hermes-agent.git /tmp/hermes \
&& cd /tmp/hermes/web \
&& npm install --silent \
&& npm run build \
&& cp -r ../hermes_cli/web_dist /build/web_dist \
&& rm -rf /tmp/hermes

# ── Hermes TUI ────────────────────────────────────────────────────────
# Build the TUI bundle (dist/entry.js) and copy to hermes_cli/tui_dist
# so the dashboard's embedded chat tab can spawn the TUI without a full workspace
RUN git clone --depth 1 --branch ${HERMES_VERSION} https://github.com/NousResearch/hermes-agent.git /tmp/hermes-tui \
&& cd /tmp/hermes-tui/ui-tui \
&& npm install --silent \
&& npm run build \
&& mkdir -p /build/tui_dist \
&& cp dist/entry.js /build/tui_dist/ \
&& rm -rf /tmp/hermes-tui

# ── Python builder: Hermes Agent venv ────────────────────────────────────
# Using 3.11-slim because hermes-agent requires Python >=3.11.
# We copy the full Python 3.11 runtime into the final image so venv works.
FROM python:3.11-slim AS python-builder

ARG HERMES_VERSION

# Install git for cloning hermes-agent repo
RUN apt-get update && apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/*

# Clone hermes-agent source (separate RUN so the prebuilt bundles can be
# injected into the tree BEFORE pip install below)
RUN git clone --depth 1 --branch ${HERMES_VERSION} https://github.com/NousResearch/hermes-agent.git /tmp/hermes

# Inject the prebuilt TUI bundle into the source tree before `pip install .`.
# pyproject.toml declares tui_dist/**/* as package-data, so with the bundle
# present the wheel is built WITH it and the venv becomes self-contained —
# the dashboard's embedded chat PTY resolves hermes_cli/tui_dist/entry.js
# at runtime, and no last-minute copy into site-packages is needed.
COPY --from=node-builder /build/tui_dist/ /tmp/hermes/hermes_cli/tui_dist/

# Build Hermes Agent Python venv (non-editable so venv is self-contained)
# IMPORTANT: venv path must match COPY destination in final stage for shebangs to work
RUN mkdir -p /usr/local/lib/hermes-agent \
&& cd /tmp/hermes \
&& python -m venv /usr/local/lib/hermes-agent/venv \
&& /usr/local/lib/hermes-agent/venv/bin/pip install --no-cache-dir . \
&& /usr/local/lib/hermes-agent/venv/bin/pip install --no-cache-dir ".[acp]" \
&& /usr/local/lib/hermes-agent/venv/bin/hermes acp --check \
&& rm -rf /tmp/hermes

# ── Final stage ────────────────────────────────────────────────────────
FROM mcr.microsoft.com/devcontainers/base:ubuntu

# Tool versions — passed from workflow via --build-arg (single source of truth)
ARG HERMES_VERSION
ARG OMNIROUTE_VERSION
ARG NODE_VERSION
ARG MNEMON_VERSION

# Export as env so install scripts can see them
ENV OLLAMA_VERSION=${OLLAMA_VERSION}
ENV OLLAMA_NO_START=1
ENV DEBIAN_FRONTEND=noninteractive

# Copy the Ollama binary from the official image
COPY --from=ollama-bin --chown=1000:1000 /usr/bin/ollama /usr/local/bin/ollama

# ── System packages + ALL heavy installs in ONE layer ──────────────────.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
zsh ripgrep jq curl git ca-certificates gnupg zstd sudo \
python3 python3-pip nodejs npm \
&& rm -rf /var/lib/apt/lists/* \

# ── Ensure a non-root user with UID 1000 exists ──────────────────────
&& UID1000_USER=$(getent passwd 1000 | cut -d: -f1) \
&& if [ -z "$UID1000_USER" ]; then \
(getent group 1000 >/dev/null || groupadd --gid 1000 vscode) \
&& useradd --uid 1000 --gid 1000 -m -s /bin/bash vscode \
&& UID1000_USER=vscode; \
fi \
&& echo "${UID1000_USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/vscode \
&& chmod 0440 /etc/sudoers.d/vscode \

# ── Ollama (direct binary install – reliable in Docker) ───────────
&& ollama --version \

# ── Bake Ollama embedding model into image ───────────────────────
# Pull nomic-embed-text at build time so it's available instantly
# at runtime with zero RAM spike or download delay. (~300MB)
# Ensure full directory tree exists with proper permissions for vscode user
&& mkdir -p /usr/share/ollama/.ollama/models \
&& chmod -R a+rwX /usr/share/ollama/.ollama \
&& chmod a+rx /usr/share/ollama \
&& OLLAMA_HOST=0.0.0.0 OLLAMA_MODELS=/usr/share/ollama/.ollama/models ollama serve & \
for i in 1 2 3 4 5; do sleep 10; curl -sf http://localhost:11434/api/tags && break; done; \
OLLAMA_MODELS=/usr/share/ollama/.ollama/models /usr/local/bin/ollama pull nomic-embed-text && \
ls -la /usr/share/ollama/.ollama/models/ && \
find /usr/share/ollama/.ollama/models -type f | head -20 && \
OLLAMA_MODELS=/usr/share/ollama/.ollama/models ollama list && \
kill %1 && wait 2>/dev/null || true \
\
# ── TailScale ─────────────────────────────────────────────────────
#&& mkdir -p /var/run/tailscale /var/lib/tailscale \
#&& (curl -fsSL https://tailscale.com/install.sh | bash || echo "WARN: tailscale install failed, continuing") \
#&& rm -rf /var/lib/apt/lists/* \


# ── Mnemon ────────────────────────────────────────────────────────
&& ARCH=amd64 \
&& curl -sL "https://github.com/mnemon-dev/mnemon/releases/download/v${MNEMON_VERSION}/mnemon_${MNEMON_VERSION}_linux_${ARCH}.tar.gz" \
-o /tmp/mnemon.tar.gz \
&& tar xzf /tmp/mnemon.tar.gz -C /tmp \
&& cp /tmp/mnemon /usr/local/bin/mnemon \
&& chmod +x /usr/local/bin/mnemon \
&& rm -rf /tmp/mnemon.tar.gz /tmp/mnemon \


&& curl -fsSL https://claude.ai/install.sh | bash \


# ── Final cleanup ─────────────────────────────────────────────────
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /root/.npm /tmp/* /var/tmp/* \
&& rm -rf /root/.cache/pip 2>/dev/null || true \
# Ensure Ollama model directory and all parent dirs are writable by vscode user
&& mkdir -p /usr/share/ollama/.ollama/models \
&& chmod -R a+rwX /usr/share/ollama/.ollama \
# Ensure /usr/share/ollama itself is traversable by vscode user
&& chmod a+rx /usr/share/ollama

# ── Copy Node.js packages from builder stage ──────────────────────────
COPY --from=node-builder /build/modelrelay/ /usr/local/lib/modelrelay/
COPY --from=node-builder /build/omniroute/ /usr/local/lib/omniroute/
# The venv's non-editable install puts hermes_cli in site-packages, so
# web_dist must go there for the dashboard --skip-build to find it.
COPY --from=node-builder /build/web_dist/ /usr/local/lib/hermes-agent/venv/lib/python3.11/site-packages/hermes_cli/web_dist/

# ── Copy Hermes Agent venv from python-builder ────────────────────────
# Non-editable install: venv is self-contained, no source tree needed.
# Venv was built at /usr/local/lib/hermes-agent/venv — same path in final image.
COPY --from=python-builder /usr/local/lib/hermes-agent/venv/ /usr/local/lib/hermes-agent/venv/

# ── Install Python 3.11 (required by hermes-agent and the venv) ──────
# The venv was built in python:3.11-slim which puts python at /usr/local/bin/python3.11.
# deadsnakes PPA installs to /usr/bin/python3.11, so symlink for compatibility.
RUN apt-get update \
&& apt-get install -y --no-install-recommends software-properties-common \
&& add-apt-repository -y ppa:deadsnakes/ppa \
&& apt-get update \
&& apt-get install -y --no-install-recommends python3.11 python3.11-venv \
&& ln -sf /usr/bin/python3.11 /usr/local/bin/python3.11 \
&& ln -sf /usr/bin/python3.11 /usr/local/bin/python \
&& rm -rf /var/lib/apt/lists/*

# ── Create symlinks for Node.js packages ──────────────────────────────
RUN ln -sf /usr/local/lib/modelrelay/bin/modelrelay /usr/local/bin/modelrelay \
&& ln -sf /usr/local/lib/omniroute/bin/omniroute /usr/local/bin/omniroute

# ── Create hermes symlink from python-builder venv ────────────────────
RUN ln -sf /usr/local/lib/hermes-agent/venv/bin/hermes /usr/local/bin/hermes

# ── Ensure hermes venv is world-readable ───────────────────────────────
# FHS root layout places the venv at /usr/local/lib/hermes-agent/venv/.
# Some sub-directories may be mode 700 (root-only). Make them traversable
# so the vscode user can exec hermes and its bundled Python. The venv is
# also made group/other-WRITABLE so the VS Code extension's ACP dependency
# repair flow (pip install into the venv as the vscode user) can succeed
# without sudo — this is a devcontainer, not a hardened prod image.
RUN chmod -R a+rX /usr/local/lib/hermes-agent 2>/dev/null || true \
&& chmod -R a+rwX /usr/local/lib/hermes-agent/venv 2>/dev/null || true \
&& chmod -R a+rX /usr/local/lib/nodejs 2>/dev/null || true

# ── Make claude CLI accessible if installed to /root ───────────────────
# The claude.ai installer may put the binary under $HOME/.local/bin/.
# Create a symlink in /usr/local/bin/ so all users can find it.
RUN CLAUDE_BIN="$(find /root/.local /usr/local -name claude -type f 2>/dev/null | head -1)" \
&& if [ -n "$CLAUDE_BIN" ] && [ ! -x "/usr/local/bin/claude" ]; then \
ln -sf "$CLAUDE_BIN" /usr/local/bin/claude \
&& echo "claude CLI linked from $CLAUDE_BIN"; \
fi

# ── Copy config files ─────────────────────────────────────────────────
COPY .devcontainer/CLAUDE.md /usr/local/share/devcontainer-config/CLAUDE.md
COPY .devcontainer/claude-term-settings.json /usr/local/share/devcontainer-config/claude-term-settings.json
COPY .devcontainer/.claude.json /usr/local/share/devcontainer-config/.claude.json
COPY .devcontainer/skill-memory-automation.md /usr/local/share/devcontainer-config/skill-memory-automation.md
COPY .devcontainer/.hermes.md /usr/local/share/devcontainer-config/.hermes.md
COPY .devcontainer/cline-globalState.json /usr/local/share/devcontainer-config/cline-globalState.json
COPY .devcontainer/cline-secrets.json /usr/local/share/devcontainer-config/cline-secrets.json
COPY .devcontainer/self-check.sh /usr/local/bin/self-check.sh

# ── Entrypoint: lightweight service start + config placement ──────────
COPY .devcontainer/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/self-check.sh

# ── Switch to non-root user (by UID, works with any username) ────────────
USER 1000

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["sleep", "infinity"]
Loading