metis-desk/scripts/visual-audit-volume.py

72 lines
5.2 KiB
Python
Raw Normal View History

#!/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()