diff --git a/AI_PATH_B_EXECUTION_PLAN.md b/AI_PATH_B_EXECUTION_PLAN.md index 78ca836..24ef15f 100644 --- a/AI_PATH_B_EXECUTION_PLAN.md +++ b/AI_PATH_B_EXECUTION_PLAN.md @@ -9,7 +9,7 @@ > state) and [`AI_CAPABILITIES_ROADMAP.md`](./AI_CAPABILITIES_ROADMAP.md) > (everything else). > -> **Status:** proposed. Not started. Decision document. +> **Status:** week 1 shipped (2026-04-28). Tool surface is live in code; image build on Coolify host + DNS wildcard + Traefik wiring still pending. > > **Why this exists:** today's AI loop is *3–7 min to first preview, 2–4 > min per iteration*, because every change goes through a Coolify nixpacks @@ -156,42 +156,45 @@ becomes: ## 5. Week-by-week execution -### Week 1 — Foundations (dev container + shell) +### Week 1 — Foundations (dev container + shell) — **SHIPPED 2026-04-28** **Goal:** AI can clone a repo, install deps, run a script. -- [ ] Build `ghcr.io/vibnai/vibn-dev:latest` Docker image (Ubuntu 24.04 + toolchains). Push to a registry the Coolify host can pull from. -- [ ] Add `lib/dev-container.ts`: helpers to mint, locate, ensure-running, suspend, resume the per-project `vibn-dev` Coolify service. -- [ ] Add MCP tool `dev_container.ensure { projectId }` — internal/auto, spins up the container if not present. Returns its UUID + status. -- [ ] Add MCP tools: `shell.exec`, `fs.read`, `fs.write`, `fs.list`, `fs.delete`, `fs.glob`, `fs.grep`. All proxy through Coolify's exec API to the dev container. -- [ ] Smoke test (`scripts/smoke-path-b.ts`): boots a dev container, clones a Gitea repo, runs `npm init -y && npm install lodash`, reads `package.json`, succeeds. -- [ ] Update `vibn-tools.ts` and ship a chat UI that streams `shell.exec` stdout to the user as it happens (existing terminal-style component if we have one, or a new one). +- [x] `vibn-dev/Dockerfile` (Ubuntu 24.04 + git + ripgrep + python3 + mise lazy toolchains). `setup-on-coolify.sh` builds it on the host; compose uses `pull_policy: never` to avoid registry round-trips. +- [x] `lib/dev-container.ts`: ensure / exec / suspend / resume helpers. Backed by `fs_project_dev_containers` (auto-created). +- [x] `devcontainer.{ensure,status,suspend}` MCP tools. +- [x] `shell.exec` + `fs.{read,write,edit,list,delete,glob,grep}` MCP tools — all enforce per-workspace tenancy via `fs_projects` ownership lookup, all locked to `/workspace`. +- [x] Network isolation: per-project `vibn-dev-net-${slug}` bridge — no route to `vibn-postgres` / `vibn-frontend`. +- [x] Kill switch: `/api/admin/path-b/{disable,enable}` flips a feature flag in <10s. +- [x] `vibn-tools.ts`: 11 new Gemini tool defs, smoke test passes (63 tools accepted). +- [x] System prompt rewritten — shell-first guidance, `gitea_file_*` flagged for hard removal in week 3. -**Exit criteria:** an internal user can chat *"clone the express hello-world repo and run it"* and see the output stream live. +**Still pending for week 1 exit:** build the image on the live Coolify host (`ssh + setup-on-coolify.sh`), end-to-end verify `devcontainer.ensure → shell.exec ls` against a real project once the frontend deploy lands. -### Week 2 — Preview URLs + iteration +### Week 2 — Preview URLs + iteration — **PARTIALLY SHIPPED 2026-04-28** **Goal:** AI starts a dev server, user clicks a preview URL, sees their app. -- [ ] Traefik wildcard rule on the Coolify host: `*.preview.vibnai.com` → terminates TLS, forwards to dev container based on subdomain (`preview-{ws}-{project}.vibnai.com` → `vibn-dev` of that project's Coolify project). -- [ ] Add MCP tools: `dev_server.start`, `dev_server.stop`, `dev_server.list`. Implementation: starts the process inside the dev container under a small supervisor (e.g. `tini` / `supervisord`), tracks PID/port, registers a Traefik label on the dev container. -- [ ] Add `fs.edit` (Aider-format search/replace, with explicit error when `oldString` not found / ambiguous). -- [ ] Per-workspace plan-tier resource caps on the dev container (free tier: 1 GB / 0.5 CPU; paid: 4 GB / 2 CPU). -- [ ] System prompt rewrite (see §4). Update the AI's deploy recipes to start with `shell.exec` and `dev_server.start` rather than `apps_create`. +- [ ] DNS: `*.preview.vibnai.com → coolify-host-ip` in OpenSRS. **Manual step, not yet done.** +- [ ] Traefik wildcard cert via DNS-01 against OpenSRS. **Config staged in `vibn-dev/PREVIEWS.md`, not yet applied to live Traefik.** +- [x] `dev_server.{start,stop,list,logs}` MCP tools. Process is `nohup`'d inside the container, PID/port/preview-url tracked in `fs_dev_servers`. Server is reachable from inside the container today; Traefik label injection is **deferred** (see PREVIEWS.md for the recommended pre-allocated-port-range approach). +- [x] `fs.edit` Aider-style (HTTP 404 if missing, 409 if ambiguous, success returns replacement count). +- [x] Per-container CPU/RAM caps: 1 vCPU / 1 GiB by default. Tier scaling via env var. +- [x] System prompt rewritten with shell-first recipe. -**Exit criteria:** the marketplace scenario from §1 works end-to-end up through "user makes 5 styling changes in 3 minutes." +**Exit criteria progress:** end-to-end works inside the container; preview URL routing is the last mile. -### Week 3 — Ship-it path + cleanup +### Week 3 — Ship-it path + cleanup — **PARTIALLY SHIPPED 2026-04-28** **Goal:** the dev container's working tree graduates to production. -- [ ] Add `ship` tool: runs `git add . && git commit -m {msg} && git push` inside the container, then either calls `apps_deploy { uuid }` (if a prod app exists) or `apps_create { projectId, repo }` (first ship). -- [ ] Auto-link the prod app to `fs_project_resources` so the existing project-isolation accounting stays consistent. -- [ ] Idle-suspend logic: cron job every 5 min checks last `shell.exec` timestamp per dev container; suspends after 30 min idle. Auto-resume on next call. -- [ ] Deprecation pass: mark `gitea_file_*` tools as deprecated in `vibn-tools.ts` (keep working, add a banner in their description). -- [ ] Update `AI_CAPABILITIES.md` to reflect the new architecture and tool surface. +- [x] `ship` MCP tool: `git init` (if needed) → `git add -A && git commit && git push` to Gitea using the workspace bot PAT, then triggers `deployApplication` if the project has a linked Coolify app. +- [x] Auto-push autosave to `vibn-autosave/main` branch (force-push, throttled to once per 5 min). Endpoint: `POST /api/admin/path-b/autosave { projectId | sweep:true }`. +- [x] Idle-suspend sweep: `POST /api/admin/path-b/idle-sweep[?minutes=30]`. Wire to a 5-min cron once we trust the suspend path. +- [ ] Hard-remove `gitea_file_*` from the AI tool list (keep REST endpoints alive 30 days). **Deferred to next week so we can A/B the new tools first.** +- [ ] Update `AI_CAPABILITIES.md`. **Deferred — will rewrite once eval data is in.** -**Exit criteria:** end-to-end clean run: user prompt → AI scaffolds in dev container → user iterates → user says "ship" → app live on real domain. Total time logged. +**Exit criteria progress:** ship loop is functionally complete. Outstanding: full prod test against a real project, gitea_file_* hard-remove, docs refresh. ### Week 4 — Eval, polish, IDE drop-in diff --git a/vibn-dev/PREVIEWS.md b/vibn-dev/PREVIEWS.md new file mode 100644 index 0000000..6438bdb --- /dev/null +++ b/vibn-dev/PREVIEWS.md @@ -0,0 +1,95 @@ +# Preview URL routing for vibn-dev + +Goal: every `dev_server.start` returns a URL like +`https://vite-mark-marketplace-7a3f.preview.vibnai.com` that works +end-to-end (TLS, HMR, websockets) without the user touching DNS or +Coolify config. + +## Architecture + +``` + Browser + │ https://.preview.vibnai.com + ▼ + Traefik (Coolify-managed) + │ routes by Host header → matching docker label + ▼ + vibn-dev container for project X + │ HOST=0.0.0.0 PORT=3000 + ▼ + Vite / Next dev / etc. +``` + +Two pieces have to be true for this to work: + +1. **DNS wildcard** — `*.preview.vibnai.com` must resolve to the + Coolify host's public IP. Set this in Cloudflare / OpenSRS once. +2. **Traefik dynamic router** — for each running dev_server, attach + docker labels to the vibn-dev container so Traefik picks up the + subdomain → port mapping. + +## DNS step (one-time) + +In OpenSRS (or whichever DNS we use for vibnai.com): + +``` +*.preview.vibnai.com. IN A +``` + +Cert: Traefik will solve a wildcard via DNS-01 against the same DNS +provider. Add a DNS provider env to Coolify's Traefik: + +```yaml +# already in our Coolify Traefik config +- "--certificatesresolvers.letsencrypt.acme.dnschallenge=true" +- "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=opensrs" +``` + +(Traefik supports OpenSRS via `OPENSRS_USERNAME` + `OPENSRS_PASSWORD` +env vars.) + +## Traefik labels (per dev_server) + +When a dev_server starts on port `P` with subdomain `S`, we update +the vibn-dev compose file with these labels and re-deploy the service: + +```yaml +labels: + - "traefik.enable=true" + - "traefik.http.routers.vibn-dev-${S}.rule=Host(`${S}.preview.vibnai.com`)" + - "traefik.http.routers.vibn-dev-${S}.entrypoints=https" + - "traefik.http.routers.vibn-dev-${S}.tls=true" + - "traefik.http.routers.vibn-dev-${S}.tls.certresolver=letsencrypt" + - "traefik.http.services.vibn-dev-${S}.loadbalancer.server.port=${P}" +``` + +## What's deferred + +The current `dev_server.start` records the URL and PID but does NOT +yet update the Coolify compose to add the Traefik labels — that +requires an `updateService` call against Coolify's API with the +revised compose YAML, which round-trips through Coolify's deployment +pipeline (~30s). For week 1 we keep dev servers reachable from inside +the container (`shell.exec curl http://localhost:PORT`) so the AI can +verify they boot, and we'll wire the Traefik label injection in week 2 +once we've decided whether to: + +(a) bake the labels into the compose at `ensureDevContainer` time + (one Traefik router per fixed port range, e.g. 3000-3010), OR +(b) hot-update the compose on each `dev_server.start` (more flexible + but slower and racier). + +Recommendation: (a) — pre-allocate router rules for ports 3000-3010 +on container creation. Simpler, faster, no compose churn. Limit dev +servers per project to 10. + +## Troubleshooting + +- "Host header mismatch" → check the dev server is actually binding + to 0.0.0.0 (not localhost). Some frameworks default to localhost + even with `HOST=0.0.0.0` env; pass `--host 0.0.0.0` to the cli. +- HMR websocket fails → Vite needs `server.hmr.clientPort: 443` and + `server.hmr.host: .preview.vibnai.com`. Document this in + the AI system prompt for week 2. +- 404 from Traefik → labels didn't apply. Check `docker inspect + vibn-dev-` and verify the labels are present. diff --git a/vibn-dev/setup-on-coolify.sh b/vibn-dev/setup-on-coolify.sh new file mode 100755 index 0000000..e30c0df --- /dev/null +++ b/vibn-dev/setup-on-coolify.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Build the vibn-dev image on the Coolify host so every project's +# docker-compose can reference `vibn-dev:latest` with pull_policy: never. +# +# Run this ONCE per Coolify host before the first chat session uses +# Path B. Re-run whenever you bump the Dockerfile. +# +# Usage (from a workstation): +# scp -r vibn-dev/ root@:/tmp/ +# ssh root@ 'bash /tmp/vibn-dev/setup-on-coolify.sh' +# +# Or run via the Coolify SSH backbone (vibn-logs user has docker access): +# ssh -i ~/.ssh/coolify_logs vibn-logs@ \ +# "cd /tmp/vibn-dev && docker build -t vibn-dev:latest ." + +set -euo pipefail + +cd "$(dirname "$0")" + +echo "Building vibn-dev:latest on $(hostname)..." +docker build -t vibn-dev:latest . + +echo +echo "Done. Image:" +docker images vibn-dev:latest --format 'table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}' + +echo +echo "Smoke test (should print 'ok'):" +docker run --rm vibn-dev:latest bash -c 'rg --version > /dev/null && git --version > /dev/null && echo ok' + +echo +echo "vibn-dev is ready. New AI dev containers will reference this image" +echo "via 'image: vibn-dev:latest' + 'pull_policy: never' in their compose." diff --git a/vibn-frontend b/vibn-frontend index 4ba9407..41d4d37 160000 --- a/vibn-frontend +++ b/vibn-frontend @@ -1 +1 @@ -Subproject commit 4ba9407534ddfd14a9aa8bfbbc79727500e8032d +Subproject commit 41d4d3748f4e42448cbc964f46f481afa1b90fba