7329cb096a
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.
119 lines
5.4 KiB
Docker
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"]
|