113 lines
4.4 KiB
Bash
113 lines
4.4 KiB
Bash
|
|
#!/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 <URL-real>'
|