# Runtime-only docker-worker image for a PACKED npm install (no repo checkout,
# no TypeScript source to build from). A published termfleet package ships
# only a pre-built dist/ and public/build/ (package.json's `files` allowlist)
# plus this Dockerfile/docker-compose.yml/docker/ — so unlike the dev
# checkout's `Dockerfile` (which compiles src/ + frontend/ in a build stage),
# this one just installs the already-published dependency graph and copies
# the already-built artifacts straight in. @termfleet/core is a REAL npm
# dependency here (not the workspace symlink a checkout resolves) — `npm
# install` pulls its own pre-built dist from the registry like any other
# dependency.
#
# Resolved and built by src/server/docker-worker-assets.ts
# (resolveDockerWorkerAssets) whenever the running process finds no
# source-build Dockerfile at the package root (a packed install ships only
# this one). See docker-compose.yml for the -f/--project-directory invocation
# this expects.
FROM node:22-bookworm-slim AS runtime

ARG TERMFLEET_BUILD_SHA=unknown
ENV TERMFLEET_BUILD_SHA=${TERMFLEET_BUILD_SHA}
ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright

RUN apt-get update \
  && apt-get install -y --no-install-recommends ca-certificates curl gpg \
  && mkdir -p /etc/apt/keyrings \
  && curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg -o /etc/apt/keyrings/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 \
  && curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /etc/apt/keyrings/pgdg.gpg \
  && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] https://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
  && apt-get update \
  && apt-get install -y --no-install-recommends tini tmux procps git openssh-client less bash gh python3 make g++ unzip \
     postgresql-17 postgresql-17-pgvector \
  && rm -rf /var/lib/apt/lists/* /var/lib/postgresql

RUN curl -fsSL https://bun.sh/install | BUN_INSTALL=/usr/local bash

COPY docker/tunnel-client.ts docker/tunnel-package.json /app/tunnel/
RUN cd /app/tunnel && mv tunnel-package.json package.json && npm install --omit=dev
COPY docker/volter-tunnel /usr/local/bin/volter-tunnel
RUN chmod +x /usr/local/bin/volter-tunnel
COPY docker/termfleet-entrypoint.sh /usr/local/bin/termfleet-entrypoint
RUN chmod +x /usr/local/bin/termfleet-entrypoint

ENV HOME=/home/termfleet
ENV NODE_ENV=production
ENV NPM_CONFIG_UPDATE_NOTIFIER=false
ENV TERMFLEET_USER=termfleet
WORKDIR /app

RUN npm install -g npm@11.12.1

RUN useradd --create-home --shell /bin/bash --uid 1001 termfleet \
  && mkdir -p /workspace /home/termfleet/.npm \
  && chown -R termfleet:termfleet /home/termfleet \
  && chown -R termfleet:termfleet /workspace \
  && chmod 1777 /workspace

# The published package.json's own dependency graph — @termfleet/core resolves
# to the REAL published package here (there is no workspace to symlink into),
# so its dist/ arrives via this install like any other dependency; no
# build-stage copy needed. `npm install`, not `npm ci`: npm's `files`
# allowlist can never ship package-lock.json (npm excludes it unconditionally,
# regardless of `files`), so a packed install has no lockfile to `ci` against.
COPY package.json ./
COPY patches ./patches
RUN npm install --ignore-scripts --omit=dev --legacy-peer-deps \
  && npm rebuild @homebridge/node-pty-prebuilt-multiarch \
  && npx patch-package

RUN npm install -g @anthropic-ai/claude-code @openai/codex \
  && npm cache clean --force

RUN npm install -g playwright@1.60.0 \
  && playwright install --with-deps chromium \
  && chmod -R 755 /ms-playwright \
  && npm cache clean --force

COPY dist ./dist
COPY public ./public

USER termfleet

EXPOSE 7373

# tini as PID 1 so orphaned children are reaped. Without it the provider itself is
# PID 1 and never wait()s, so zombies accumulate indefinitely (kiiro: 363 and still
# climbing, 2026-08-06). A zombie reads as ALIVE to naive kill(pid,0) liveness
# checks, so any lockfile holding its PID looks permanently held — which is what
# wedged .volter/runtime/cycle.lock and blocked the Volter autonomy loop.
# CMD is intentionally unchanged, so `docker run ... <override>` still works.
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/termfleet-entrypoint"]
CMD ["node", "/app/dist/cli.js", "provider", "serve", "--kind", "virtual-tmux", "--host", "0.0.0.0", "--port", "7373", "--prefix", "termfleet-docker", "--count", "0", "--cwd", "/workspace", "--reap-ended-after", "300", "--max-windows", "32"]
