# OpenClaw on Hugging Face Spaces — Pre-built image (v4.4) # Changelog: # v4.2 — sudo access, Homebrew (brew), uv, Go added for agent tooling # v4.3 — skill dependencies added (wrong go paths — fixed in v4.4) # v4.4 — FIXED: correct go module paths (all tools are by @steipete); # gh via official GitHub apt repo; gemini via npm; nano-pdf via pip; # steipete/tap added for brew; OpenClaw always pulls :latest # ── Stage 1: Pull pre-built OpenClaw ───────────────────────────────────────── # :latest always fetches the newest release on every rebuild. # If you ever need to pin a version use e.g. :2026.3.13 FROM ghcr.io/openclaw/openclaw:latest AS openclaw-prebuilt # ── Stage 2: Runtime ───────────────────────────────────────────────────────── FROM node:22-bookworm SHELL ["/bin/bash", "-c"] # ── System deps + apt-installable skill tools (root) ───────────────────────── # Skills unlocked by apt: session-logs+trello (jq, ripgrep), tmux, video-frames (ffmpeg) # gh added via official GitHub apt repo — more reliable than brew on Linux RUN echo "[build] Installing system deps + gh..." && START=$(date +%s) \ && apt-get update \ && apt-get install -y --no-install-recommends \ git ca-certificates curl wget python3 python3-pip \ sudo build-essential procps file \ jq ripgrep tmux ffmpeg \ poppler-utils tesseract-ocr \ # ── GitHub CLI (gh) via official apt repo ── && mkdir -p -m 755 /etc/apt/keyrings \ && wget -qO /etc/apt/keyrings/githubcli-archive-keyring.gpg \ https://cli.github.com/packages/githubcli-archive-keyring.gpg \ && chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ > /etc/apt/sources.list.d/github-cli.list \ && apt-get update \ && apt-get install -y --no-install-recommends gh \ && rm -rf /var/lib/apt/lists/* \ && gh --version \ # ── Python + pip skill tools ── && pip3 install --no-cache-dir --break-system-packages \ huggingface_hub requests nano-pdf openai-whisper ffmpeg-python \ && corepack enable \ && mkdir -p /app/openclaw \ && chown -R node:node /app \ && mkdir -p /home/node/.openclaw/workspace /home/node/.openclaw/credentials \ && chown -R node:node /home/node \ # ── Passwordless sudo for node user ── && echo "node ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/node \ && chmod 0440 /etc/sudoers.d/node \ && echo "[build] System deps: $(($(date +%s) - START))s" # ── Go language + Go-based skill tools (root) ───────────────────────────────── # All CLIs are by Peter Steinberger (@steipete) — correct module paths: # gifgrep → github.com/steipete/gifgrep/cmd/gifgrep # blucli → github.com/steipete/blucli/cmd/blu # eightctl → github.com/steipete/eightctl/cmd/eightctl # songsee → github.com/steipete/songsee/cmd/songsee # gog → github.com/steipete/gogcli (binary: gog) RUN echo "[build] Installing Go + Go-based skill CLIs..." && START=$(date +%s) \ && GO_VER=$(curl -fsSL "https://go.dev/VERSION?m=text" | head -1) \ && curl -fsSL "https://dl.google.com/go/${GO_VER}.linux-amd64.tar.gz" -o /tmp/go.tar.gz \ && tar -C /usr/local -xzf /tmp/go.tar.gz \ && rm /tmp/go.tar.gz \ && /usr/local/go/bin/go version \ && GOBIN=/usr/local/bin /usr/local/go/bin/go install \ github.com/steipete/gifgrep/cmd/gifgrep@latest 2>&1 || echo "[go] WARN: gifgrep" \ && GOBIN=/usr/local/bin /usr/local/go/bin/go install \ github.com/steipete/blucli/cmd/blu@latest 2>&1 || echo "[go] WARN: blucli/blu" \ && GOBIN=/usr/local/bin /usr/local/go/bin/go install \ github.com/steipete/eightctl/cmd/eightctl@latest 2>&1 || echo "[go] WARN: eightctl" \ && GOBIN=/usr/local/bin /usr/local/go/bin/go install \ github.com/steipete/songsee/cmd/songsee@latest 2>&1 || echo "[go] WARN: songsee" \ && GOBIN=/usr/local/bin /usr/local/go/bin/go install \ github.com/steipete/gogcli@latest 2>&1 || echo "[go] WARN: gogcli/gog" \ && echo "[build] Go ${GO_VER} + CLIs: $(($(date +%s) - START))s" # ── uv (fast Python package manager by Astral) ─────────────────────────────── RUN echo "[build] Installing uv..." && START=$(date +%s) \ && curl -LsSf https://astral.sh/uv/install.sh | UV_INSTALL_DIR=/usr/local/bin sh \ && uv --version \ && echo "[build] uv: $(($(date +%s) - START))s" # ── npm skill tools (root) ──────────────────────────────────────────────────── # Skills unlocked: clawhub, mcporter, oracle, gemini RUN echo "[build] Installing npm skill tools..." && START=$(date +%s) \ && npm install -g \ @anthropic-ai/claude-code \ acpx@latest \ clawhub \ mcporter \ @steipete/oracle \ @google/gemini-cli \ && echo "[build] npm tools: $(($(date +%s) - START))s" # ── Copy pre-built OpenClaw ─────────────────────────────────────────────────── COPY --from=openclaw-prebuilt --chown=node:node /app /app/openclaw # ── Switch to node user ─────────────────────────────────────────────────────── USER node ENV HOME=/home/node WORKDIR /app # ── Homebrew (Linux) — must run as non-root ─────────────────────────────────── RUN echo "[build] Installing Homebrew..." && START=$(date +%s) \ && NONINTERACTIVE=1 /bin/bash -c \ "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" \ && echo "[build] Homebrew: $(($(date +%s) - START))s" ENV HOMEBREW_PREFIX=/home/node/.linuxbrew ENV HOMEBREW_CELLAR=/home/node/.linuxbrew/Cellar ENV HOMEBREW_REPOSITORY=/home/node/.linuxbrew/Homebrew ENV HOMEBREW_NO_ANALYTICS=1 ENV HOMEBREW_NO_AUTO_UPDATE=1 # ── Brew skill tools (steipete/tap + standard formulas) ────────────────────── # - First add steipete/tap (source of gifgrep, songsee, blucli etc.) # - Then install formulas — each is soft-fail (|| echo) so build never breaks # if a formula isn't available on Linux homebrew-core RUN echo "[build] Installing brew skill tools..." && START=$(date +%s) \ && BREW=/home/node/.linuxbrew/bin/brew \ && $BREW tap steipete/tap 2>&1 || echo "[brew] WARN: steipete/tap tap failed" \ && install_brew() { \ $BREW install "$@" \ && echo "[brew] ✓ $*" \ || echo "[brew] ✗ $* (skipped)"; \ } \ # Standard homebrew-core formulas && install_brew himalaya \ && install_brew 1password-cli \ && install_brew gogcli \ && install_brew openhue \ && install_brew ordercli \ && install_brew wacli \ && install_brew xurl \ && install_brew summarize \ && install_brew obsidian-cli \ && install_brew camsnap \ && install_brew openai-whisper \ # steipete/tap formulas (fallback if go install above didn't work) && install_brew steipete/tap/gifgrep \ && install_brew steipete/tap/songsee \ && install_brew steipete/tap/blucli \ && install_brew steipete/tap/eightctl \ && echo "[build] brew skill tools: $(($(date +%s) - START))s" # ── A2A Gateway Extension ───────────────────────────────────────────────────── RUN echo "[build] Installing A2A gateway..." && START=$(date +%s) \ && git clone --depth 1 https://github.com/win4r/openclaw-a2a-gateway.git /app/openclaw/extensions/a2a-gateway \ && cd /app/openclaw/extensions/a2a-gateway \ && npm install --production \ && echo "[build] A2A gateway: $(($(date +%s) - START))s" # ── Coding Agent Extension ──────────────────────────────────────────────────── COPY --chown=node:node extensions/coding-agent /app/openclaw/extensions/coding-agent # ── Pre-configure acpx ──────────────────────────────────────────────────────── RUN mkdir -p /home/node/.acpx \ && echo '{"defaultAgent":"claude","defaultPermissions":"approve-all","format":"text"}' \ > /home/node/.acpx/config.json # ── npm user-local prefix ───────────────────────────────────────────────────── RUN mkdir -p /home/node/.npm-global \ && npm config set prefix /home/node/.npm-global # ── Prepare runtime dirs ────────────────────────────────────────────────────── RUN mkdir -p /app/openclaw/empty-bundled-plugins \ && node -e "try{console.log(require('/app/openclaw/package.json').version)}catch(e){console.log('unknown')}" > /app/openclaw/.version \ && echo "[build] OpenClaw version: $(cat /app/openclaw/.version)" # ── Scripts + Config + Frontend ─────────────────────────────────────────────── COPY --chown=node:node scripts /home/node/scripts COPY --chown=node:node frontend /home/node/frontend COPY --chown=node:node workspace-templates /home/node/workspace-templates COPY --chown=node:node openclaw.json /home/node/scripts/openclaw.json.default RUN chmod +x /home/node/scripts/entrypoint.sh /home/node/scripts/sync_hf.py \ && VERSION_TS=$(date +%s) \ && sed "s/{{VERSION_TIMESTAMP}}/${VERSION_TS}/g" /home/node/frontend/electron-standalone.html > /home/node/frontend/index.html \ && echo "[build] Frontend index.html generated (timestamp=${VERSION_TS})" # ── Environment ─────────────────────────────────────────────────────────────── ENV NODE_ENV=production ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/openclaw/empty-bundled-plugins ENV OPENCLAW_PREFER_PNPM=1 ENV NPM_CONFIG_PREFIX=/home/node/.npm-global ENV GOPATH=/home/node/go # Full explicit PATH — user bins first, then system ENV PATH=/home/node/.npm-global/bin:/home/node/.linuxbrew/bin:/home/node/.linuxbrew/sbin:/home/node/.local/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin WORKDIR /home/node CMD ["/home/node/scripts/entrypoint.sh"]