diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d49fc9d --- /dev/null +++ b/.env.example @@ -0,0 +1,28 @@ +# AI Provider +AI_PROVIDER_BASE_URL=https://your-ai-provider.example.com/v1/ +AI_PROVIDER_API_KEY=your-api-key-here + +# OpenCode Server +OPENCODE_SERVER_PASSWORD=your-opencode-server-password + +# GitHub +GITHUB_TOKEN=your-github-token-here + +# Context7 MCP +CONTEX7_API_KEY=your-context7-api-key-here + +# Telegram (optional) +TELEGRAM_API_ID=your-telegram-api-id +TELEGRAM_API_HASH=your-telegram-api-hash +TELEGRAM_BOT_TOKEN=your-telegram-bot-token + +# Redis (optional) +REDIS_PASSWORD=your-redis-password + +# Cloudflare Tunnel (optional — see separate tunnel repo) +CLOUDFLARE_TUNNEL_TOKEN= + +# Antidetect Browser MCP +# Local: http://localhost:8765/mcp +# Remote: http://:8765/mcp +ANTIDETECT_BROWSER_MCP_URL=http://localhost:8765/mcp \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3a65583 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +FROM node:20-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + git \ + openssh-client \ + autossh \ + python3 \ + make \ + g++ \ + chromium \ + gnupg \ + docker.io \ + ffmpeg \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -LsSf https://astral.sh/uv/install.sh | UV_INSTALL_DIR=/usr/local/bin sh + +RUN mkdir -p -m 0755 /etc/apt/keyrings \ + && curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | gpg --dearmor -o /etc/apt/keyrings/githubcli-archive-keyring.gpg \ + && chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ + && mkdir -p /etc/apt/sources.list.d \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ + && apt-get update \ + && apt-get install -y gh \ + && rm -rf /var/lib/apt/lists/* + +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true + +RUN npm install -g opencode-ai repomix --unsafe-perm + +WORKDIR /root/workspace + +EXPOSE 4096 + +ENTRYPOINT ["opencode"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7dc2e57 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,62 @@ +services: + dind: + image: docker:dind + container_name: opencode-docker-dind + privileged: true + environment: + - DOCKER_TLS_CERTDIR= + networks: + - opencode_network + restart: unless-stopped + deploy: + resources: + limits: + cpus: '2' + memory: 4G + pids: 512 + reservations: + memory: 1G + + opencode: + build: + context: . + dockerfile: Dockerfile + container_name: opencode + restart: unless-stopped + command: ["serve", "--hostname", "0.0.0.0", "--port", "4096", "--cors", "*"] + working_dir: /root/workspace + env_file: + - .env + environment: + - DOCKER_HOST=tcp://docker-dind:2375 + - OPENCODE_CONFIG_DIR=/root/.config/opencode + - OPENCODE_MEMORY_DIR=/root/workspace/app_data/opencode-memory + - DOCKER_CONTAINER=true + - GIT_AUTHOR_NAME=opencode-agent + - GIT_AUTHOR_EMAIL=agent@opencode.local + - GIT_COMMITTER_NAME=opencode-agent + - GIT_COMMITTER_EMAIL=agent@opencode.local + volumes: + - ./.opencode:/root/.config/opencode + - ./app_data:/root/.local/share/opencode + - ./app_data/workspaces:/root/workspace + - ./app_data/ssh:/root/.ssh:ro + ports: + - "127.0.0.1:4096:4096" + networks: + - opencode_network + depends_on: + dind: + condition: service_started + deploy: + resources: + limits: + cpus: '3' + memory: 6G + pids: 1024 + reservations: + memory: 2G + +networks: + opencode_network: + name: opencode_network \ No newline at end of file diff --git a/docs/decisions/004-pr-24-docker-setup.md b/docs/decisions/004-pr-24-docker-setup.md new file mode 100644 index 0000000..8c91d22 --- /dev/null +++ b/docs/decisions/004-pr-24-docker-setup.md @@ -0,0 +1,30 @@ +# ADR-004: Docker setup (public, cleaned) + +## Статус +Accepted + +## Контекст +Docker-based AI coding assistant с persistent memory. Старый репо `/root/workspace/opencode/` содержал `cloudflared` (Cloudflare Tunnel client), external network `digital_factory_app_network` (shared с monorepo digital_factory), приватные IPs (`80.85.139.21`), приватный домен `ai.slaid098.dev` в `AI_PROVIDER_BASE_URL`, tunnel UUID `f97e66a5-...` в комментариях `.env.example`. Публичный релиз `slaid098/opencode-config` требует очистки от приватной инфраструктуры. + +Архитектура нового репо: 4 bind mount'а (`.opencode`/`app_data`/`app_data/workspaces`/`app_data/ssh:ro`) вместо старого `config/`+`sandbox_workspace`. + +## Решение +- 2 services в `docker-compose.yml`: `dind` (docker:dind, privileged, 2 CPU / 4G) + `opencode` (build from Dockerfile, 3 CPU / 6G) +- Network `opencode_network` (self-contained, НЕ external) +- 4 bind mounts: `./.opencode`→`/root/.config/opencode`, `./app_data`→`/root/.local/share/opencode`, `./app_data/workspaces`→`/root/workspace`, `./app_data/ssh`→`/root/.ssh:ro` +- `docker-compose.yml`: `env_file: .env` (gitignored, user copies from `.env.example`) +- `Dockerfile`: node:20-slim + apt (ca-certificates, curl, git, openssh-client, autossh, python3, make, g++, chromium, gnupg, docker.io, ffmpeg) + uv + gh (via apt keyring) + `npm install -g opencode-ai repomix --unsafe-perm` + Puppeteer ENV (system chromium) +- `.env.example`: placeholder only (`your-api-key-here`, `https://your-ai-provider.example.com/v1/`) +- Убрано: `cloudflared` install (Dockerfile строки 33-34 оригинала), `digital_factory_app_network` external network, IP `80.85.139.21`, домен `ai.slaid098.dev`, tunnel UUID `f97e66a5-...` +- `GIT_AUTHOR/COMMITTER_EMAIL` изменён с `agent@slaid098.dev` на `agent@opencode.local` + +## Альтернативы +- **Сохранить cloudflared** — отклонено. Публичный репо, tunnel = приватная инфраструктура (named tunnel `slaid098-dev` привязан к конкретному Cloudflare account). Tunnel вынесен в отдельный приватный репо. +- **Docker compose без dind** — отклонено. opencode-ai использует Docker для sandboxed execution (субагенты запускают `docker run`), без dind теряется ключевая возможность. +- **Сохранить external network `digital_factory_app_network`** — отклонено. External network shared с monorepo `digital_factory` на конкретной машине, не self-contained. Заменён на локальную `opencode_network`. +- **Hardcode приватных IPs/domains в `.env.example`** — отклонено. Публичный репо, секреты в `.env` (gitignored), `.env.example` — документация с placeholder'ами. + +## Последствия +- `docker compose config` без `.env` падает с `env file .env not found` — expected, пользователь обязан `cp .env.example .env` перед `docker compose up`. +- `OPENCODE_MEMORY_DIR=/root/workspace/app_data/opencode-memory` — путь внутри контейнера маппится через bind mount в `./app_data/workspaces/app_data/opencode-memory` на хосте. Layout может потребовать корректировки после runtime-тестирования. +- AGENTS.md bind-mount volume не включён в этот PR — добавляется в #10. \ No newline at end of file diff --git a/docs/handoff/pr-24-docker-setup.md b/docs/handoff/pr-24-docker-setup.md new file mode 100644 index 0000000..8fd8fff --- /dev/null +++ b/docs/handoff/pr-24-docker-setup.md @@ -0,0 +1,29 @@ +# PR: Docker setup + +## Что сделано +- docker-compose.yml: 2 services (dind + opencode), 4 bind mounts, opencode_network +- Dockerfile: node:20-slim, uv, gh, chromium, docker.io, opencode-ai, repomix +- .env.example: cleaned of private IPs/domains/tunnel UUID, placeholder only +- Убрано: cloudflared (tunnel → отдельный репо), digital_factory_app_network (self-contained network) + +## Почему +Docker-based AI coding assistant. Публичный релиз — очищено от приватной инфраструктуры. +Source: `/root/workspace/opencode/` (ветка fix/pipeline-status/review-next-by-verdict) — docker-compose.yml, Dockerfile, .env.example. + +## Pending +- AGENTS.md bind-mount volume — добавляется в #10 + +## Watch out +- `.env` gitignored — пользователь создаёт из `.env.example` (`cp .env.example .env`). `docker compose config` без `.env` падает с `env file .env not found` — это expected, не баг. +- .env.example НЕ содержит реальных ключей — placeholder only. +- CLOUDFLARE_TUNNEL_TOKEN= (optional) — оставлен как placeholder, tunnel перенесён в отдельный репо. +- ANTIDETECT_BROWSER_MCP_URL=http://localhost:8765/mcp — дефолт local, для remote переопределить в `.env` на `http://:8765/mcp`. +- GIT_AUTHOR/COMMITTER_EMAIL изменён с `agent@slaid098.dev` на `agent@opencode.local` — публичный репо без приватного домена. +- OPENCODE_MEMORY_DIR=/root/workspace/app_data/opencode-memory — путь внутри контейнера; bind mount `./app_data/workspaces:/root/workspace` маппит host→container, так что memory лежит в `./app_data/workspaces/app_data/opencode-memory` на хосте. Возможен нюанс с layout — проверить в runtime. + +## Файлы +- `docker-compose.yml` — 2 services (dind, opencode), network `opencode_network`, 4 bind mounts +- `Dockerfile` — node:20-slim + apt (ca-certs, curl, git, ssh, autossh, python3, make, g++, chromium, gnupg, docker.io, ffmpeg) + uv + gh + opencode-ai + repomix +- `.env.example` — AI provider, OpenCode server, GitHub, Context7, Telegram, Redis, Cloudflare (optional), Antidetect Browser MCP +- `docs/handoff/pr-24-docker-setup.md` — этот файл +- `docs/decisions/004-pr-24-docker-setup.md` — ADR-004 \ No newline at end of file diff --git a/docs/project-map/README.md b/docs/project-map/README.md index 70a77e1..033481c 100644 --- a/docs/project-map/README.md +++ b/docs/project-map/README.md @@ -1,6 +1,6 @@ # Project Map -opencode-config — Docker-based AI coding assistant with persistent memory (opencode configuration). +opencode-config — Docker-based AI coding assistant with persistent memory (opencode configuration). Runs in Docker via `docker-compose.yml` (dind + opencode services). ## Structure @@ -81,6 +81,9 @@ opencode-config/ ├── pyproject.toml # Python project (uv, ruff, pytest config) ├── uv.lock # Locked deps for Python project ├── .pre-commit-config.yaml # ruff + UV hooks +├── docker-compose.yml # 2 services (dind + opencode), opencode_network, 4 bind mounts — PR#24 +├── Dockerfile # node:20-slim + uv + gh + chromium + docker.io + opencode-ai + repomix — PR#24 +├── .env.example # Placeholder-only env template (user copies to .env) — PR#24 ├── app_data/ │ ├── workspaces/ # Agent working directory (.gitkeep) │ └── ssh/ # SSH keys, not in git (.gitkeep)