Files
Haozhe Wu 7329cb096a release: 0.1.14 — a backtest you can read, and Intel Macs that can install
Rolls up 272 commits / 74 merged pull requests since 0.1.13.

Run Detail grows four analysis tabs (factor research, positions structure,
tearsheet, research dashboard) and the Web UI gains an Options Lab, all
reading artifacts a run already writes. `smartmoneyconcepts` moves to an
opt-in `[smc]` extra so an Intel-Mac install stops becoming a CMake source
build for llvmlite. Strategy Discovery lands both phases; scheduled research
delivers itself through a leased outbox and persists each monitor's verdict.

MCP grows to 74 tools. Six READMEs carry the release news and the 30
contributors of this cycle.

Two published version strings had no guard and were still on 0.1.13 after
every other declaration moved: `agent/SKILL.md`, the manifest ClawHub serves
and no build step touches because it is not in the wheel, and the Docker
image's `org.opencontainers.image.version` label. The whole suite stayed
green. `test_release_version_consistency.py` now enumerates all eleven
declaration sites against pyproject rather than spot-checking remembered
ones, and globs the locale directory so a new language is covered the day it
lands. The desktop shell keeps its own 0.3.0 track on purpose — it has no
updater, and 0.3.1 is issue #1016's validation target, not a shipped build.
2026-08-20 07:49:22 +08:00

119 lines
5.4 KiB
Docker

# ============================================================================
# Stage 1: Build frontend
# ============================================================================
FROM node:22-slim@sha256:6c74791e557ce11fc957704f6d4fe134a7bc8d6f5ca4403205b2966bd488f6b3 AS frontend-build
# node:22-slim digest resolved 2026-07-28
WORKDIR /app/frontend
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci --ignore-scripts
COPY frontend/ ./
RUN npm run build
# ============================================================================
# Stage 2: Python builder — compiles wheels + builds a self-contained venv.
# build-essential lives ONLY here; it never reaches the runtime image.
# ============================================================================
FROM python:3.11-slim@sha256:e031123e3d85762b141ad1cbc56452ba69c6e722ebf2f042cc0dc86c47c0d8b3 AS builder
# python:3.11-slim digest resolved 2026-07-13
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Isolated venv we can copy wholesale into the runtime stage.
ENV VIRTUAL_ENV=/opt/venv
RUN python -m venv "$VIRTUAL_ENV"
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
WORKDIR /app
# Python deps first for layer caching. Installed from the hash-pinned lock
# (agent/requirements.txt is the human-edited source; regenerate the lock
# with the command documented at the top of requirements-lock.txt whenever
# agent/requirements.txt changes).
COPY agent/requirements.txt agent/requirements.txt
COPY requirements-lock.txt requirements-lock.txt
RUN pip install --no-cache-dir --require-hashes -r requirements-lock.txt
# Channel SDKs (feishu + telegram) come from their own hash-pinned lock, not
# from `pip install -e ".[feishu,telegram]"`. An extras install resolves against
# whatever PyPI serves at build time with no hashes, which would quietly opt the
# image out of the contract the line above establishes. To change the channel
# set, edit agent/requirements-channels.txt and regenerate the lock with the
# command documented at the top of that file.
COPY requirements-channels-lock.txt requirements-channels-lock.txt
RUN pip install --no-cache-dir --require-hashes -r requirements-channels-lock.txt
# Copy project + install the CLI entrypoint (editable — the runtime stage
# re-creates the same /app/agent source tree the .pth file points at).
# --no-deps because every dependency is already installed from the two locks
# above; without it pip re-resolves and downloads unhashed wheels.
COPY pyproject.toml LICENSE README.md ./
COPY agent/ agent/
RUN pip install --no-cache-dir --no-deps -e .
# ============================================================================
# Stage 3: Runtime — carries the prebuilt venv only, no compilers/dev headers.
# ============================================================================
FROM python:3.11-slim@sha256:e031123e3d85762b141ad1cbc56452ba69c6e722ebf2f042cc0dc86c47c0d8b3 AS runtime
# python:3.11-slim digest resolved 2026-07-13
LABEL org.opencontainers.image.title="Vibe-Trading" \
org.opencontainers.image.description="Natural-language finance research AI agent with backtesting" \
org.opencontainers.image.version="0.1.14" \
org.opencontainers.image.source="https://github.com/HKUDS/Vibe-Trading" \
org.opencontainers.image.licenses="MIT"
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# Runtime-only native libs. NO build-essential here — these are weasyprint's
# shared libraries (Pango/HarfBuzz/Fontconfig/Cairo/gdk-pixbuf) per its official
# Debian install list; without them the lazy `from weasyprint import HTML` in
# reporter.py fails and PDF rendering silently downgrades to HTML-only.
# fonts-dejavu-core gives non-blank PDFs.
RUN apt-get update && apt-get install -y --no-install-recommends \
libpango-1.0-0 \
libpangoft2-1.0-0 \
libharfbuzz0b \
libfontconfig1 \
libgdk-pixbuf-2.0-0 \
libcairo2 \
fonts-dejavu-core \
&& rm -rf /var/lib/apt/lists/*
# Bring in the prebuilt venv from the builder stage.
ENV VIRTUAL_ENV=/opt/venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
COPY --from=builder /opt/venv /opt/venv
# Re-materialize the source tree the editable install references, plus the
# built frontend static assets.
COPY pyproject.toml LICENSE README.md ./
COPY agent/ agent/
COPY --from=frontend-build /app/frontend/dist frontend/dist
# Runtime should not run as root. `vibe` owns the writable app-data dirs so
# named volumes inherit usable permissions. `vibe-sandbox` is an unprivileged
# system account (no home, no shell) that runner.py drops into via
# subprocess.run(user="vibe-sandbox") to execute LLM-generated code with the
# least privilege — created here by fixed contract, not otherwise used.
RUN useradd --create-home --shell /usr/sbin/nologin vibe \
&& useradd --system --no-create-home --shell /usr/sbin/nologin --uid 10001 vibe-sandbox \
&& mkdir -p agent/runs agent/sessions agent/uploads agent/.swarm/runs /home/vibe/.vibe-trading \
&& chown -R vibe:vibe /app /home/vibe/.vibe-trading
USER vibe
# Default port
EXPOSE 8899
# Health check — hits /live (liveness probe; /health remains a legacy alias).
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8899/live')" || exit 1
# Run API server (serves frontend/dist as static files)
CMD ["vibe-trading", "serve", "--host", "0.0.0.0", "--port", "8899"]