#!/usr/bin/env bash # Alternativa explícita si el cliente no carga el MCP de proyecto. set -euo pipefail script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) exec python3 -c "$(cat <<'PY' import json import os import pathlib import stat import shutil import sys import tomllib root = pathlib.Path(sys.argv[1]).resolve() executable = shutil.which(os.environ.get('METIS_CODEX_BIN', 'codex')) if executable is None: sys.exit('Error: instala Codex CLI en WSL o indica METIS_CODEX_BIN.') with (root / '.codex/config.toml').open('rb') as source: config = tomllib.load(source)['mcp_servers']['playwright'] state_path = os.environ.get('METIS_PLAYWRIGHT_STORAGE_STATE') if state_path: state = (root / state_path).resolve() try: metadata = state.stat() except OSError: sys.exit('Error: sesión ausente o inválida; vuelve a iniciar scripts/visual-audit.py.') if (not state.is_relative_to(root / '.artifacts/playwright') or not stat.S_ISREG(metadata.st_mode) or metadata.st_uid != os.getuid() or stat.S_IMODE(metadata.st_mode) & 0o077): sys.exit('Error: la sesión debe ser un archivo propio y privado dentro de .artifacts/playwright.') try: data = json.loads(state.read_text()) except (OSError, ValueError): sys.exit('Error: sesión inválida; vuelve a iniciar scripts/visual-audit.py.') if not isinstance(data, dict) or not isinstance(data.get('cookies'), list): sys.exit('Error: formato de sesión Playwright inválido.') cookies = data['cookies'] if not cookies or any(not isinstance(cookie, dict) or cookie.get('domain') not in ('localhost', '127.0.0.1') or cookie.get('name') != 'metis.sid' or cookie.get('httpOnly') is not True for cookie in cookies): sys.exit('Error: solo se admiten sesiones locales de auditoría de Metis.') args = list(config['args']) image_position = next(index for index, arg in enumerate(args) if arg.startswith('mcr.microsoft.com/playwright/mcp:')) args[image_position:image_position] = [ '--user=' + str(os.getuid()) + ':' + str(os.getgid()), '--mount=type=bind,source=' + str(state) + ',target=/tmp/metis-audit-state.json,readonly', ] args.append('--storage-state=/tmp/metis-audit-state.json') config['args'] = args overrides = [] for key in ('command', 'args', 'startup_timeout_sec', 'tool_timeout_sec'): overrides.extend(['-c', 'mcp_servers.playwright.' + key + '=' + json.dumps(config[key])]) overrides.extend(['-c', 'mcp_servers.playwright.enabled=true']) # exec conserva terminal, señales y código de salida; no copia configuración ni credenciales. os.execv(executable, [executable, '-C', str(root), *overrides, *sys.argv[2:]]) PY )" "$script_dir/.." "$@"