#!/usr/bin/env bash # Prepara el entorno Docker fijado; no instala navegadores ni modifica Codex global. set -euo pipefail if [[ "${1:-}" == "--help" ]]; then printf '%s\n' 'Uso: bash scripts/setup-playwright-wsl.sh' \ 'Requiere WSL2, Python >=3.11 y Docker Linux accesible.' \ 'Reutiliza la imagen fijada o la descarga; comprueba Chromium y red host.' exit 0 fi if [[ $# -ne 0 ]]; then printf '%s\n' 'Error: argumento desconocido; utiliza --help.' >&2 exit 2 fi script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) repo_dir=$(cd -- "$script_dir/.." && pwd) kernel_release=$(uname -r) if [[ "${kernel_release,,}" != *microsoft* || "${kernel_release,,}" != *wsl2* ]]; then printf '%s\n' 'Error: ejecuta este instalador desde bash dentro de WSL2.' >&2 exit 1 fi for executable in python3 docker timeout; do if ! command -v "$executable" >/dev/null 2>&1; then printf 'Error: falta %s. Consulta docs/playwright-mcp.md.\n' "$executable" >&2 exit 1 fi done if ! python3 -c 'import sys; sys.exit(sys.version_info < (3, 11))'; then printf '%s\n' 'Error: se necesita Python 3.11 o posterior (tomllib).' >&2 exit 1 fi trap 'printf "Error: preparación interrumpida en la línea %s. Revisa Docker y docs/playwright-mcp.md.\n" "$LINENO" >&2' ERR METIS_MCP_IMAGE=$(python3 - "$repo_dir/.codex/config.toml" <<'PY' import re import sys import tomllib with open(sys.argv[1], 'rb') as source: config = tomllib.load(source)['mcp_servers']['playwright'] images = [arg for arg in config['args'] if re.fullmatch(r'mcr\.microsoft\.com/playwright/mcp:v[\d.]+@sha256:[a-f0-9]{64}', arg)] if config['command'] != 'docker' or len(images) != 1 or '--network=host' not in config['args']: sys.exit('Error: se requiere la configuración Docker oficial fijada y red host.') print(images[0]) PY ) if ! timeout 30 docker info --format '{{.OSType}}' | python3 -c 'import sys; sys.exit(sys.stdin.read().strip() != "linux")'; then printf '%s\n' 'Error: Docker Linux no responde. Activa el daemon o la integración WSL; comprueba los permisos del usuario.' >&2 exit 1 fi if timeout 30 docker image inspect "$METIS_MCP_IMAGE" >/dev/null 2>&1; then printf '%s\n' 'La imagen fijada ya está disponible; se reutiliza.' else printf '%s\n' 'Descargando servidor y Chromium compatibles en la misma imagen oficial…' timeout 600 docker pull "$METIS_MCP_IMAGE" fi # El endpoint efímero solo devuelve un token de prueba; no sirve archivos del equipo. python3 - "$METIS_MCP_IMAGE" <<'PY' import http.server import subprocess import sys import threading import uuid token = uuid.uuid4().hex container = 'metis-playwright-setup-' + token[:12] class ProbeHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.end_headers() self.wfile.write(token.encode()) def log_message(self, format, *args): pass code = """const { chromium } = require('playwright'); (async () => { const response = await fetch(process.argv[1], {signal: AbortSignal.timeout(10000)}); if (!response.ok || await response.text() !== process.argv[2]) { throw new Error('La red host no alcanza WSL.'); } const browser = await chromium.launch({channel: 'chromium', headless: true, args: ['--no-sandbox']}); try { console.log(JSON.stringify({chromium: browser.version(), network: 'WSL loopback OK'})); } finally { await browser.close(); } })().catch(error => {console.error(error.message); process.exitCode = 1;});""" try: with http.server.HTTPServer(('127.0.0.1', 0), ProbeHandler) as server: threading.Thread(target=server.serve_forever, daemon=True).start() try: subprocess.run([ 'docker', 'run', '--rm', '--init', '--name=' + container, '--network=host', '--shm-size=1g', '--workdir=/app', '--entrypoint=node', sys.argv[1], '-e', code, 'http://127.0.0.1:' + str(server.server_port), token, ], check=True, timeout=90) finally: server.shutdown() except (OSError, subprocess.SubprocessError) as error: sys.exit('Error: fallo al validar Chromium/red host. Revisa Docker Desktop y sus permisos. ' + str(error)) finally: subprocess.run(['docker', 'rm', '-f', container], capture_output=True, timeout=15, check=False) PY printf '%s\n' 'Entorno preparado. No se ha modificado la configuración global de Codex.' \ 'Arranca Metis y ejecuta: python3 scripts/check-playwright-mcp.py '