Strips runtime packages (nodejs_22, kubectl, jq, git, emacs, tsx, tea, pythonEnv, qmd) from the Docker image contents, reducing image size from ~2.7GB to ~1.5GB.
Key changes:
- Removed 9 runtime packages from contents (moved to openclaw-runtime-closure)
- Removed pythonEnv let binding and qmd parameter (no longer needed in image)
- Added OPENCLAW_RUNTIME_CLOSURE env var (bakes closure path for init container)
- Added runtime closure bin dir to PATH (resolves after PVC population)
- Added curl to contents (needed by init container for Harmonia health checks)
- CI: added openclaw-runtime-closure to build-and-cache PACKAGES array
- CI: added second sed command for CronJob image tag update
- CI: removed inherit qmd from openclaw-image callPackage (qmd now in runtime closure)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Creates openclaw-runtime-closure as a symlinkJoin of all runtime dependencies (nodejs_22, kubectl, jq, git, emacs, tsx, tea, python3.withPackages pymupdf, qmd) that will be pushed to the Harmonia binary cache by CI and pulled into the PVC by the init container at pod startup.
This meta-package enables the thin Docker image architecture where:
- The Docker image stays small (~1.5GB vs 2.7GB) with only /app, nix, and basics
- Runtime deps are fetched from Harmonia at pod startup via nix copy --from
- CI push is fast (thin image) and runtime deps are cached separately
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- packages/qmd: buildNpmPackage with Node.js 22 (not Bun) to avoid
native module ABI issues with better-sqlite3 and sqlite-vec
- Vendored package-lock.json (QMD ships bun.lock, not npm lockfile)
- packages/openclaw-image: adds qmd + tsx to image contents
- packages/default.nix: rec attrset so openclaw-image can inherit qmd
- flake.nix: expose custom-qmd package output for CI caching
The OpenClaw runtime validates that resolved symlinks stay within
/app/dist/extensions/. When /app was a Nix store symlink, realpath
resolved to /nix/store/ which 'escaped' the boundary. Now we copy
the app files into /app as a real directory in extraCommands.