#!/usr/bin/env python3 """Amplía únicamente la instalación desechable de visual-audit.py mediante HTTP real. Requiere METIS_PLAYWRIGHT_STORAGE_STATE. Repetir reutiliza registros por nombre. No lee .env ni almacena credenciales; la sesión privada sigue perteneciendo al preparador. """ import json import os from pathlib import Path import runpy import tomllib from urllib.parse import urlparse ROOT = Path(__file__).resolve().parent.parent AUDIT = runpy.run_path(str(ROOT / "scripts/visual-audit.py")) def main(): value = os.environ.get("METIS_PLAYWRIGHT_STORAGE_STATE") if not value: raise RuntimeError("Requiere sesión del preparador desechable.") state = (ROOT / value).resolve() if not state.is_relative_to(ROOT / ".artifacts/playwright") or state.stat().st_mode & 0o077: raise RuntimeError("La sesión debe ser privada y estar en el directorio de auditoría.") report = json.loads((state.parent / "report.json").read_text()) if urlparse(report["origin"]).hostname != "localhost" or report.get("ordinaryUser") is not True: raise RuntimeError("Requiere instalación sintética local con usuario ordinario.") config = tomllib.loads((ROOT / ".codex/config.toml").read_text())["mcp_servers"]["playwright"] with AUDIT["browser"](config, state) as client: code = """async (page) => { const origin=__AUDIT_ORIGIN__, company=__AUDIT_COMPANY__; const session=await page.request.get(origin+'/api/auth/session'); if(session.status()!==200)throw Error('Sesión no disponible'); const {csrfToken}=await session.json(); async function get(path){const r=await page.request.get(origin+'/api'+path);if(r.status()!==200)throw Error('GET '+path+': '+r.status());return r.json()} async function post(path,data){const r=await page.request.post(origin+'/api'+path,{data,headers:{origin,'x-csrf-token':csrfToken}});if(r.status()!==201)throw Error('POST '+path+': '+r.status());return r.json()} const companies=(await get('/companies')).companies; if(!companies.some(c=>c.id===company&&c.name==='Auditoría visual — Datos sintéticos'))throw Error('La compañía no es la fixture del preparador'); for(let i=1;i<=14;i++){const name='AUDIT-COMPANY-'+String(i).padStart(2,'0');if(!companies.some(c=>c.name===name))await post('/companies',{name,active:i%5!==0})} const details=await get('/companies/'+company); const people=details.people??details.company?.people; if(!Array.isArray(people))throw Error('No se reconoce la colección de personas'); const catalog=(await get('/asset-catalog/'+company)).items; const laptop=catalog.find(c=>c.values.kind==='template'&&c.values.name==='Portátil'); const printer=catalog.find(c=>c.values.kind==='template'&&c.values.name==='Impresora'); if(!laptop||!printer)throw Error('Faltan plantillas de activos'); const assets=(await get('/assets/'+company)).assets; const resourceCatalog=(await get('/resource-catalog/'+company)).items; const application=resourceCatalog.find(c=>c.builtinKey==='template:application'); if(!application)throw Error('Falta plantilla de aplicación'); const resources=(await get('/resources/'+company)).resources; for(let i=1;i<=50;i++){ const suffix=String(i).padStart(2,'0'); const person='AUDIT-PERSON-'+suffix+(i%10===0?' — Álvaro Muñoz de la Peña':''); if(!people.some(p=>p.name===person))await post('/companies/'+company+'/people',{name:person,position:i%3?'Soporte':null,email:'person'+suffix+'@audit.example.test',phone:null,active:i%7!==0,isContact:i%4===0}); const name='AUDIT-ASSET-'+suffix+(i%10===0?' — Equipo de documentación e ingeniería':''); if(!assets.some(a=>a.values.name===name))await post('/assets/'+company,{values:{name,templateId:i%5===0?printer.id:laptop.id,siteId:null,registeredOn:i%3?'2026-01-15':null,status:i%7===0?'retired':'operational',notes:i%10===0?'Notas sintéticas de tamaño representativo. '.repeat(15):'',characteristics:{}}}); const resource='AUDIT-VOLUME-APP-'+suffix; if(!resources.some(r=>r.values.name===resource))await post('/resources/'+company,{values:{name:resource,templateId:application.id,active:i%7!==0,notes:i%10===0?'Aplicación sintética para pruebas con grandes volúmenes. '.repeat(15):'',characteristics:{}}}); } const inventory=await get('/assets/'+company); const finalDetails=await get('/companies/'+company); return {companies:(await get('/companies')).companies.length,people:(finalDetails.people??finalDetails.company.people).length,assets:inventory.assets.length,resources:(await get('/resources/'+company)).resources.length,companyId:company}; }""".replace("__AUDIT_ORIGIN__", json.dumps(report["origin"])).replace("__AUDIT_COMPANY__", json.dumps(report["companyId"])) result = AUDIT["result_json"](AUDIT["tool"](client, "browser_run_code_unsafe", {"code": code})) (state.parent / "volume.json").write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n") print(json.dumps(result, ensure_ascii=False)) if __name__ == "__main__": main()