Skip to content

feat(forge): 4 tools nuevas para V — remote_execution + browser_control + image_generation + ssh_command_executor - #47

Merged
turbillon50 merged 3 commits into
mainfrom
claude/v-server-tools-clean
May 16, 2026
Merged

feat(forge): 4 tools nuevas para V — remote_execution + browser_control + image_generation + ssh_command_executor#47
turbillon50 merged 3 commits into
mainfrom
claude/v-server-tools-clean

Conversation

@turbillon50

Copy link
Copy Markdown
Owner

Qué resuelve

V está reportando Unknown tool: image_generation (y similar para ssh_command_executor, browser_control). Eso es porque las tools NO están en main — el PR #46 las tenía pero estaba sucio (arrastraba cambios de claude/v-vision-cross-device sin 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 via V_SERVER_URL env (default http://178.105.135.26:5000). Maneja 404, timeout y connection refused con mensajes claros — nunca pretende que jaló.

lib/forge/tools.ts

4 tools nuevas al array TOOLS + 4 cases al dispatch():

Tool Endpoint Hetzner Ring Estado server
remote_execution /execute 1 ✅ vivo
browser_control /browser 1 ⏳ pendiente (Playwright)
image_generation /generate-image 1 ⏳ pendiente (OpenRouter / Gemini Image)
ssh_command_executor /ssh-execute 2 ⏳ pendiente (paramiko)

redactInput reforzado con SENSITIVE_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.ts

SENIOR_ENGINEER_DOCTRINE extendido con dos bloques nuevos:

  • CUERPO EN HETZNER: IP, endpoints disponibles vs pendientes, regla "si tool falla con 'endpoint no existe aún', repórtalo a Luis, no inventes".
  • SSH A SERVIDORES REMOTOS: ring 2, describir antes de destructivos, credenciales nunca en chat, errores literales no inventados.

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 con remote_execution (snippet completo con backup y rollback) o Luis con SSH manual.

Cómo activar después del merge

  1. Mergear este PR → V ya tiene las tools.
  2. V (con remote_execution) redeploya api.py siguiendo docs/v-server/README.md Opción A.
  3. Luis o V agrega OPENROUTER_API_KEY al systemd unit (systemctl edit v-server).
  4. V prueba image_generation con 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).
  • Healthcheck del servidor: curl http://178.105.135.26:5000/health{"status":"healthy"} (versión actual v1; tras redeploy será v2).

Riesgos / TODOs post-merge

  • TLS pendiente: el servidor escucha en HTTP plano. Antes de uso real con credenciales SSH reales: nginx + Let's Encrypt delante del Flask.
  • Auth pendiente: setear V_SERVER_TOKEN para que solo V pueda llamar (no cualquiera con la IP).
  • ssh_command_executor es 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

…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
@vercel

vercel Bot commented May 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vforge Ready Ready Preview, Comment May 16, 2026 3:37pm

Request Review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread lib/forge/v-server.ts
Comment on lines +38 to +41
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread lib/forge/tools.ts
Comment on lines +3389 to +3392
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) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread docs/v-server/api.py
Comment on lines +191 to +196
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}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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
@turbillon50
turbillon50 merged commit dc3f6b7 into main May 16, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants