feat(forge): 4 tools nuevas para V — remote_execution + browser_control + image_generation + ssh_command_executor - #47
Conversation
…ol + image_generation + ssh_command_executor Cablea V al servidor Hetzner que Luis levantó (178.105.135.26:5000, Flask blindado con systemd). V ahora tiene "manos" para ejecutar código, controlar navegadores, generar imágenes y administrar servidores remotos por SSH. NOTA: este PR reemplaza al #46 que estaba sucio (arrastraba cambios de la branch v-vision-cross-device que no han llegado a main). Este PR es CLEAN diff contra main — solo agrega lo necesario. Cambios: - lib/forge/v-server.ts (nuevo): helper callVServer() POST al Flask del Hetzner. URL via V_SERVER_URL env (default 178.105.135.26:5000). Maneja 404 ("endpoint pendiente"), timeout y connection refused con mensajes claros — nunca pretende que jaló. - lib/forge/tools.ts: 4 tools al array TOOLS + 4 cases al dispatch: · remote_execution → /execute (Python/Node, 30s) · browser_control → /browser (Playwright, 8 acciones, wait_for_selector) · image_generation → /generate-image (OpenRouter, default Gemini Image) · ssh_command_executor → /ssh-execute (paramiko, RING 2) redactInput refuerza: SENSITIVE_KEYS (password, private_key, secret, token, api_key, auth, authorization) → "[REDACTED N chars]" en audit log. - lib/forge/system-prompt.ts: SENIOR_ENGINEER_DOCTRINE extendido con bloques CUERPO EN HETZNER y SSH A SERVIDORES REMOTOS. V sabe cuándo usar cada tool nueva y qué endpoints están pendientes en api.py. - docs/v-server/api.py: Flask v2 completo con todos los endpoints listo para redeployar (Luis o V via remote_execution). OpenRouter para imágenes (Gemini 3.1 Flash Image default). - docs/v-server/install.sh: script idempotente que instala flask/paramiko/ playwright/requests + chromium en el venv. - docs/v-server/README.md: dos rutas de deploy (Opción A: V auto-deploys con remote_execution + snippet completo con backup y rollback; Opción B: Luis con SSH manual). Pendiente del lado servidor (no de este PR): redeployar api.py + setear OPENROUTER_API_KEY en el systemd unit. Snippet listo en README Opción A. Type check: 0 errores nuevos (los 2 preexistentes en main no son míos). https://claude.ai/code/session_01YD9ckFDdBjDhYB6aaH1HR4
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b3b083f8f4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const res = await fetch(url, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify(body), |
There was a problem hiding this comment.
Send V server auth token on remote tool calls
When V_SERVER_TOKEN is enabled on the Flask server, every non-/health request requires X-V-Token, but callVServer always sends only Content-Type. This makes all four new tools fail with 401 in the secured configuration described in the new docs, so the integration cannot work once auth is turned on. Add header forwarding (for example from an env var) in this request path.
Useful? React with 👍 / 👎.
| const timeoutSeconds = clampNumber(input.timeout_seconds, 1, 300, 60); | ||
| payload.timeout_seconds = timeoutSeconds; | ||
| const res = await callVServer("/ssh-execute", payload, { timeoutMs: (timeoutSeconds + 15) * 1000 }); | ||
| if (!res.ok) { |
There was a problem hiding this comment.
Enforce confirmation before executing ring-2 SSH commands
This new tool is documented as RING 2 — destructivo, but the dispatcher executes the SSH command immediately with no explicit confirmation gate (unlike existing confirmed=true checks used for other ring-2 operations). A single model-generated destructive command can therefore run on first invocation without human approval, which violates the repo’s ring protocol and creates a direct operational risk.
Useful? React with 👍 / 👎.
| if url: | ||
| page.goto(url, wait_until="domcontentloaded", timeout=30000) | ||
| if wait_for_selector: | ||
| page.wait_for_selector(wait_for_selector, timeout=10000) | ||
| if action == "goto": | ||
| result["result"] = f"navegado a {url}" |
There was a problem hiding this comment.
Reject browser goto requests that omit URL
In /browser, action == "goto" returns a success message even when url is missing, because navigation is only attempted inside if url:. This can produce false positives ("navegado a None") and cause the caller to continue automation against a blank page. goto should require a non-empty URL and return 400 otherwise.
Useful? React with 👍 / 👎.
…es url 3 P1/P2 issues reportados por Codex en PR #47: 1) [P1] lib/forge/v-server.ts:41 — callVServer no mandaba X-V-Token, así que en cuanto Luis active V_SERVER_TOKEN en el server todo da 401. Ahora lee process.env.V_SERVER_TOKEN y lo forwarda como header X-V-Token. Si la env var está vacía, sigue funcionando sin auth (modo dev actual). 2) [P1] lib/forge/tools.ts:3392 — ssh_command_executor es RING 2 pero ejecutaba sin gate de confirmación. Patrón aplicado igual que github_create_file/update_file a main: requiere input.confirmed===true. Si falta: devuelve failureCode RING2_NEEDS_CONFIRMATION + instruction para V ("describe a Luis qué vas a correr, espera 'sí', rellama con confirmed=true"). Schema actualizado, description ajustada. 3) [P2] docs/v-server/api.py:196 — /browser con action=goto y url vacío devolvía éxito ("navegado a None"). Ahora valida url al inicio del case goto y devuelve 400 si falta. Bonus: README actualizado — el comentario "necesita edición para mandar header" ya no aplica (v-server.ts lo hace solo). Aclara que hay que poner V_SERVER_TOKEN en ambos lados (Hetzner systemd + Vercel env). https://claude.ai/code/session_01YD9ckFDdBjDhYB6aaH1HR4
Estos errores ya existían en main y bloqueaban el build de cualquier PR. Como ampliación del PR para destrabar el CI: - app/api/v-skills-nuclear/route.ts: 90 ocurrencias de "ARRAY['x','y']" (sintaxis SQL Postgres) reemplazadas por "['x','y']" (array literal TS). El template tag `sql\`\`` de Neon convierte arrays JS a arrays Postgres automáticamente, no se necesita el prefijo ARRAY. - app/api/v-health/route.ts:17 — `checks: Record<string, boolean|string>` no admitía el `number` asignado en línea 43 (result[0]?.n). Tipo extendido a `boolean | string | number`. - lib/forge/tools.ts:3283 — `(input.method ?? "GET").toUpperCase()` falla porque input.method es unknown. Fixed con narrowing: `(typeof input.method === "string" ? input.method : "GET").toUpperCase()`. Verificación: tsc --noEmit → 0 errores, eslint → 0 errores (33 warnings preexistentes, no bloquean CI). https://claude.ai/code/session_01YD9ckFDdBjDhYB6aaH1HR4
Qué resuelve
V está reportando
Unknown tool: image_generation(y similar parassh_command_executor,browser_control). Eso es porque las tools NO están enmain— el PR #46 las tenía pero estaba sucio (arrastraba cambios declaude/v-vision-cross-devicesin mergear, conflictos con main, draft).Este PR es la versión LIMPIA: solo lo necesario para que V tenga las 4 tools.
Cambios
lib/forge/v-server.ts(nuevo)Helper
callVServer()que hace POST al Flask del Hetzner. URL viaV_SERVER_URLenv (defaulthttp://178.105.135.26:5000). Maneja 404, timeout y connection refused con mensajes claros — nunca pretende que jaló.lib/forge/tools.ts4 tools nuevas al array
TOOLS+ 4 cases aldispatch():remote_execution/executebrowser_control/browserimage_generation/generate-imagessh_command_executor/ssh-executeredactInputreforzado conSENSITIVE_KEYS(password, private_key, secret, token, api_key, auth, authorization) → reemplaza por[REDACTED N chars]en el audit log. Las credenciales nunca quedan en BD.lib/forge/system-prompt.tsSENIOR_ENGINEER_DOCTRINEextendido con dos bloques nuevos:docs/v-server/api.py: Flask v2 completo, listo para redeploy.install.sh: instala flask/paramiko/playwright/requests + chromium.README.md: 2 rutas — V auto-deploys conremote_execution(snippet completo con backup y rollback) o Luis con SSH manual.Cómo activar después del merge
remote_execution) redeployaapi.pysiguiendodocs/v-server/README.mdOpción A.OPENROUTER_API_KEYal systemd unit (systemctl edit v-server).image_generationcon un prompt simple — debe devolver base64.Por qué importa
Hasta hoy V podía pensar y decidir pero no ejecutar código fuera de GitHub/Vercel. Esto la convierte en agente autónomo real: probar lógica antes de comitearla, verificar UI tras deploy, generar assets gráficos, gestionar servidores por SSH.
Verificación
npx tsc --noEmit→ 0 errores nuevos en archivos tocados (los 2 errores reportados son preexistentes en main).curl http://178.105.135.26:5000/health→{"status":"healthy"}(versión actual v1; tras redeploy será v2).Riesgos / TODOs post-merge
V_SERVER_TOKENpara que solo V pueda llamar (no cualquiera con la IP).ssh_command_executores RING 2: destructivo potencial. La doctrina en system-prompt le pide a V describir antes de destructivos.Cierra: PR #46 queda obsoleto y se puede cerrar.
https://claude.ai/code/session_01YD9ckFDdBjDhYB6aaH1HR4
Generated by Claude Code