Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ node_modules
ops
scripts/*
!scripts/check-node-version.mjs
!scripts/openclaw-cli-shim.py
80 changes: 70 additions & 10 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
# ═══════════════════════════════════════════════════════════════════════════════
# Mission Control — Environment Variables
# ═══════════════════════════════════════════════════════════════════════════════
# Copy to .env and adjust for your deployment mode.
#
# IMPORTANT: NEXT_PUBLIC_* variables are baked into the client-side JavaScript
# bundle at build time (pnpm build). If you change any NEXT_PUBLIC_* variable,
# you MUST run `pnpm build` again for the change to take effect in the browser.
# Server-side variables (OPENCLAW_*, AUTH_*, etc.) are read at runtime and do
# not require a rebuild.

# MC_MODE=prod
MC_MODE=dev
# 0, если нужно без OpenClaw
OPENCLAW_ENABLED=1

# === Server Port ===
# PORT=3000

# === Make / Docker local runtime ===
# `make up` and `make status` resolve MC_URL from these keys.

# --- Mission Control: базовый URL приложения ---
# Протокол внешнего доступа к MC: http для локальной разработки, https для прод/публичного доступа.
MC_URL_SCHEME=http
# Хост, на котором доступен Mission Control.
MC_HOST=127.0.0.1
# Порт Mission Control на хосте.
MC_PORT=7012

# ═══════════════════════════════════════════════════════════════════════════════
# Authentication
# ═══════════════════════════════════════════════════════════════════════════════
Expand All @@ -28,13 +43,22 @@
# Auto-generated on first run if not set. Persisted to .data/.auto-generated.
# AUTH_SECRET=

MC_COOKIE_SECURE=
MC_COOKIE_SAMESITE=strict

# --- Опционально: безопасность Mission Control ---
# Network access control (production: blocked unless host is explicitly allowed)
# Patterns: exact "app.example.com", subdomain "*.example.com", prefix "100.*"
# MC_ALLOW_ANY_HOST=
# Разрешённые host-заголовки через запятую.
MC_ALLOWED_HOSTS=localhost,127.0.0.1,::1
# Включить HSTS (ТОЛЬКО при HTTPS, иначе можно сломать доступ по HTTP).
MC_ENABLE_HSTS=
# Secure-cookie (ТОЛЬКО при HTTPS; для HTTP оставьте пустым).
# Set to 1 only when Mission Control is served over HTTPS.
# Keep blank for local/plain-HTTP deployments.
MC_COOKIE_SECURE=

MC_COOKIE_SAMESITE=strict
# Set to 1 only when TLS terminates at your proxy/load-balancer.
# MC_ENABLE_HSTS=1


# Trusted reverse proxy / header authentication
# MC_PROXY_AUTH_HEADER=X-User-Email
Expand All @@ -57,11 +81,33 @@ NEXT_PUBLIC_GOOGLE_CLIENT_ID=
# For local development, both point to the same machine (127.0.0.1).
# For Docker or remote servers, they will differ.

# ─── Local Development ────────────────────────────────────────────────────────
# Both MC and gateway run on the same machine. No special config needed.
OPENCLAW_GATEWAY_HOST=127.0.0.1
OPENCLAW_GATEWAY_PORT=18789
# OPENCLAW_GATEWAY_TOKEN=
## ─── Local Development ────────────────────────────────────────────────────────
## Both MC and gateway run on the same machine. No special config needed.
#OPENCLAW_GATEWAY_HOST=127.0.0.1
#
## --- OpenClaw: хост для health/status проверок ---
#OPENCLAW_STATUS_HOST=127.0.0.1
#
## --- OpenClaw: внешние порты (host bind) ---
## Внешний порт OpenClaw gateway.
#OPENCLAW_GATEWAY_PORT=18789
## Внешний порт панели управления OpenClaw (Control UI).
#OPENCLAW_CONTROL_UI_PORT=18791
#
## --- OpenClaw: внутренние/bridge порты ---
## Внутренний порт gateway внутри контейнера.
#OPENCLAW_GATEWAY_INTERNAL_PORT=18789
## Внешний порт bridge на хосте.
#OPENCLAW_BRIDGE_PORT=18790
## Внутренний порт bridge внутри контейнера.
#OPENCLAW_BRIDGE_INTERNAL_PORT=18790

# --- OpenClaw: токен доступа к gateway (секрет) ---
# Секретный токен для авторизации в OpenClaw gateway.
# Никогда не публикуйте реальный токен в git.
OPENCLAW_GATEWAY_TOKEN=...


NEXT_PUBLIC_GATEWAY_HOST=
NEXT_PUBLIC_GATEWAY_PORT=18789

Expand Down Expand Up @@ -105,6 +151,12 @@ NEXT_PUBLIC_GATEWAY_PORT=18789
OPENCLAW_HOME=
# OPENCLAW_CONFIG_PATH=
OPENCLAW_TOOLS_PROFILE=coding
# Visibility of group chat replies when projecting OpenClaw state (automatic|message_tool).
OPENCLAW_MESSAGES_GROUPCHAT_VISIBLE_REPLIES=automatic
# Sandbox tooling relies on Docker; the OpenClaw stack mounts /var/run/docker.sock read/write when enabled.
# Optional: if the socket gid differs from the container's Docker group, set DOCKER_SOCKET_GID.
# When unset, the compose stack auto-detects from the mounted socket.
DOCKER_SOCKET_GID=

NEXT_PUBLIC_GATEWAY_PROTOCOL=
NEXT_PUBLIC_GATEWAY_URL=
Expand All @@ -120,6 +172,14 @@ MC_DEFAULT_GATEWAY_NAME=primary
MC_COORDINATOR_AGENT=coordinator
NEXT_PUBLIC_COORDINATOR_AGENT=coordinator

# ═══════════════════════════════════════════════════════════════════════════════
# Automation (optional)
# ═══════════════════════════════════════════════════════════════════════════════
# Enable automatic daily backups without toggling it in the UI. Accepts 1/true/yes/on.
# Backup directory will be created automatically when scheduled backups run.
# Example: MC_AUTO_BACKUP=1
MC_AUTO_BACKUP=

# ═══════════════════════════════════════════════════════════════════════════════
# Data Paths (all optional, defaults to .data/ in project root)
# ═══════════════════════════════════════════════════════════════════════════════
Expand Down
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,29 @@ playwright-report/
# Claude Code context files (root CLAUDE.md is committed for AI agent discovery)
**/CLAUDE.md
!/CLAUDE.md

# Local agent walkthrough (kept on disk, not committed)
WALKTHROUGH.md

# IDE files
/.idea/
/.vscode/

# Third-party service clones — naming pattern: <service-name>-src/
# Each is an independent repo; we only check in the compose service that
# references it, never the upstream code.
/openclaw-src/
/gpu-coordinator-proxy-src/

# Persistent state + secrets, also local-only.
/.openclaw-data/
/.mc-openclaw/
/.env.openclaw
/examples/

# Dolt database files (added by bd init)
.dolt/
*.db
/.beads/
/.beads/
/.vibe/
21 changes: 17 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ FROM base AS deps
COPY package.json ./
COPY pnpm-lock.yaml* ./
# better-sqlite3 requires native compilation tools
RUN apt-get update && apt-get install -y python3 make g++ --no-install-recommends && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get upgrade && apt-get install -y python3 make g++ --no-install-recommends && rm -rf /var/lib/apt/lists/*
RUN if [ -f pnpm-lock.yaml ]; then \
pnpm install --frozen-lockfile; \
else \
Expand All @@ -32,8 +32,20 @@ WORKDIR /app
ENV NODE_ENV=production
# curl, CA certs, python3, git needed for agent runtime installers (OpenClaw, Hermes)
# procps provides `ps` and `uptime` used by system-monitor APIs
RUN apt-get update && apt-get install -y curl ca-certificates python3 git make g++ procps --no-install-recommends && rm -rf /var/lib/apt/lists/*
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
RUN apt-get update && apt-get upgrade && apt-get install -y curl ca-certificates python3 git make g++ procps tmux jq --no-install-recommends && rm -rf /var/lib/apt/lists/*

# Bake Claude Code + Codex CLIs into the image as a fallback so the
# Settings → Agent Runtimes panel reports "Installed" even before the host
# bind-mounts (compose adds ${HOME}/.local/bin at runtime, which takes
# precedence in PATH and provides authenticated host binaries).
RUN npm install -g @anthropic-ai/claude-code @openai/codex 2>&1 | tail -5

# node:22-slim already ships a `node` user at uid 1000; reuse it as our
# `nextjs` alias so bind-mounted host files (typical Linux uid 1000) read directly.
RUN if ! id -u nextjs >/dev/null 2>&1; then \
usermod --login nextjs --move-home --home /home/nextjs node && \
groupmod --new-name nodejs node ; \
fi
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
COPY --from=build /app/public ./public
Expand All @@ -46,7 +58,8 @@ RUN mkdir -p .data && chown nextjs:nodejs .data
RUN echo 'const http=require("http");const r=http.get("http://localhost:"+(process.env.PORT||3000)+"/api/status?action=health",s=>{process.exit(s.statusCode===200?0:1)});r.on("error",()=>process.exit(1));r.setTimeout(4000,()=>{r.destroy();process.exit(1)})' > /app/healthcheck.js
COPY docker-entrypoint.sh /app/docker-entrypoint.sh
RUN chmod 755 /app/docker-entrypoint.sh && \
chmod -R a+rX /app/public/ /app/src/
chmod -R a+rX /app/public/ /app/src/ && \
chown -R nextjs:nodejs /app /home/nextjs
USER nextjs
ENV PORT=3000
EXPOSE 3000
Expand Down
81 changes: 81 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Dockerfile.dev — image for `make dev` (hot-reload, no production build).
#
# Source code is bind-mounted from the host at runtime; this image only
# bakes the OS deps, pnpm, node_modules, and the host-fallback CLIs.
# Rebuild only when the dependency manifest or system tooling changes.
FROM node:22.22.0-slim
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app

# OS deps: better-sqlite3 build tools, agent-runtime probes, /chat PTY,
# docker CLI (talks to host daemon via bind-mounted /var/run/docker.sock —
# required so `openclaw doctor` can verify sandbox readiness without
# emitting the "Docker is not available" warning to the Doctor panel).
RUN apt-get update && apt-get upgrade && apt-get install -y \
curl ca-certificates python3 git make g++ procps tmux jq docker.io \
--no-install-recommends && rm -rf /var/lib/apt/lists/*

# Bake claude / codex CLIs as a fallback. The host's ~/.local/bin still wins
# at runtime via PATH order, so an authenticated host install transparently
# shadows these baked copies.
RUN npm install -g @anthropic-ai/claude-code @openai/codex 2>&1 | tail -5

# OpenClaw CLI is provided as a bind-mounted shim, NOT a baked npm install.
# `docker-compose-dev.yml` mounts the cloned openclaw-src/ at /opt/openclaw-src,
# and this shim runs `node /opt/openclaw-src/dist/index.js "$@"`. To update
# openclaw, run `make openclaw-update` on the host — it git-pulls openclaw-src
# and rebuilds dist via the openclaw-builder container; both this shim AND the
# gateway daemon pick up the new dist on the next call (no docker rebuild).
#
# The shim ALSO transparently rewrites legacy CLI shapes that MC source uses
# but that openclaw 2026.4.x has retired. Specifically MC's wake / agent
# message endpoints call `openclaw gateway sessions_send --session X --message
# Y`, but the daemon now exposes that only as the RPC method `chat.send`
# behind `openclaw gateway call chat.send --params {...}`. Rewriting in the
# shim keeps MC unmodified while staying compatible with new openclaw.
#
# The shim itself is bind-mounted from ./scripts/openclaw-cli-shim.py via
# docker-compose-dev.yml so edits land live without rebuilding this image.
RUN cat > /usr/local/bin/openclaw <<'SHIM' && chmod +x /usr/local/bin/openclaw
#!/bin/sh
# Live-loaded openclaw CLI shim. Source: /opt/openclaw-src (bind-mounted from
# host's openclaw-src/ clone). Updates via `make openclaw-update`.
if [ ! -f /opt/openclaw-src/dist/index.js ]; then
echo "openclaw shim: /opt/openclaw-src/dist/index.js not found." >&2
echo " Did you run 'make openclaw-build' to populate openclaw-src/dist/?" >&2
exit 127
fi
if [ -f /usr/local/lib/openclaw-cli-shim.py ]; then
exec python3 /usr/local/lib/openclaw-cli-shim.py "$@"
fi
exec node /opt/openclaw-src/dist/index.js "$@"
SHIM

# Install deps from manifests only — never from source. The source tree is
# bind-mounted at runtime, so changes to .ts/.tsx/.css don't invalidate this
# layer. Only changes to package.json / pnpm-lock.yaml trigger a rebuild.
COPY package.json pnpm-lock.yaml* ./
RUN if [ -f pnpm-lock.yaml ]; then \
pnpm install --frozen-lockfile; \
else \
echo "WARN: pnpm-lock.yaml not found in build context; running non-frozen install" && \
pnpm install --no-frozen-lockfile; \
fi

# Reuse uid 1000 (the slim image's `node` user) as `nextjs`, matching prod
# image exactly so bind-mounted host files keep their ownership.
RUN if ! id -u nextjs >/dev/null 2>&1; then \
usermod --login nextjs --move-home --home /home/nextjs node && \
groupmod --new-name nodejs node ; \
fi
RUN mkdir -p .data .next && chown -R nextjs:nodejs /app /home/nextjs

USER nextjs
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
ENV NODE_ENV=development
EXPOSE 3000

# Default command — overridden by docker-compose-dev.yml. Provided here so
# the image is also runnable standalone (`docker run mission-control-dev`).
CMD ["pnpm", "exec", "next", "dev", "--hostname", "0.0.0.0", "--port", "3000"]
Loading