diff --git a/scripts/render_terminal.py b/scripts/render_terminal.py index ce731f3..8ef4d95 100644 --- a/scripts/render_terminal.py +++ b/scripts/render_terminal.py @@ -9,6 +9,37 @@ import asyncio, html, io, re from PIL import Image from playwright.async_api import async_playwright + +ANSI_COL={30:'#6e7681',31:'#ff5c6c',32:'#3ddc84',33:'#ffb454',34:'#58a6ff',35:'#d2a8ff',36:'#7fdbff',37:'#e6edf3', + 90:'#8b949e',91:'#ff7b86',92:'#56f09f',93:'#ffd479',94:'#79c0ff',95:'#e2c5ff',96:'#a5f3ff',97:'#ffffff'} +def ansi_to_html(line): + """Convert ANSI SGR colour/bold sequences to tags (everything else is stripped).""" + out=[]; open_spans=0; pos=0 + for m in re.finditer(r'\x1b\[([0-9;]*)m|\x1b\[[0-9;?]*[A-Za-z]',line): + out.append(html.escape(line[pos:m.start()])); pos=m.end() + if m.group(1) is None: continue + codes=[int(c) if c else 0 for c in m.group(1).split(';')] + for c in codes: + if c==0: out.append(''*open_spans); open_spans=0 + elif c==1: out.append(''); open_spans+=1 + elif c in ANSI_COL: out.append(f''); open_spans+=1 + out.append(html.escape(line[pos:])); out.append(''*open_spans) + return ''.join(out) + +def mask(lines, serial_cols=True, ips=True): + """Sanitize output before publishing: hide drive serials and private IPs.""" + res=[] + for l in lines: + if ips: l=re.sub(r'\b(10|192\.168|172\.(1[6-9]|2\d|3[01]))\.(\d+)\.(\d+)\.(\d+)\b','x.x.x.x',l) if not l.startswith('x') else l + if serial_cols and re.match(r'^(\d+|m2-\d)\s+/dev/',l): + parts=re.split(r'(\s{2,})',l) + # fields alternate with whitespace; serial is the 8th field (index 14 in the split list) + if len(parts)>14: + v=parts[14]; parts[14]=v[:2]+'\u2022'*max(0,len(v)-2) + l=''.join(parts) + res.append(l) + return res + DEFAULT_RULES=[(r'✓|\bOK\b|\bNORMAL\b|\bPASS(ED)?\b|HEALTH_OK','#3ddc84'),(r'⚠️?|\bWARN(ING)?\b|HEALTH_WARN|\[WARN\]','#ffb454'),(r'\bERROR\b|\bFAIL(ED)?\b|\bCRITICAL\b|✗|HEALTH_ERR','#ff5c6c'),(r'^=+.*=+$|^┏.*|^┃.*|^┣.*|^┗.*','#7fdbff')] PAGE=''' @@ -25,6 +56,7 @@ PAGE='''
%(t)s
''' def colorize(line, rules): + if '\x1b[' in line: return ansi_to_html(line) s=html.escape(line) for pat,col in rules: if pat.startswith('^'):