66import argparse
77import json
88import os
9+ import re
910import sys
10- import unicodedata
1111
1212import requests
1313
1414from lib .mc_client import MCClient , resolve_machines
1515
1616
17- def _sanitize (text ):
18- """Strip control characters except newline."""
17+ def _sanitize (text , keep_color = False ):
18+ """Strip ANSI escape sequences and control characters.
19+
20+ If keep_color is True, SGR sequences (color/formatting, ending
21+ with 'm') are preserved. All other escape sequences and control
22+ characters (except newline) are removed.
23+ """
24+ if keep_color :
25+ # Strip non-SGR escape sequences (cursor movement, erase, etc.)
26+ text = re .sub (r'\x1b\[[0-9;]*[^0-9;m]' , '' , text )
27+ else :
28+ # Strip all ANSI escape sequences
29+ text = re .sub (r'\x1b\[[0-9;]*[a-zA-Z]' , '' , text )
30+ # Strip remaining control characters except newline
1931 return '' .join (c for c in text
20- if c == '\n ' or unicodedata . category ( c )[ 0 ] != 'C' )
32+ if c == '\n ' or not ( 0 <= ord ( c ) < 32 or ord ( c ) == 127 ) )
2133
2234
2335def cmd_machines (args , mc ):
@@ -77,14 +89,15 @@ def cmd_resolve(args, mc):
7789
7890def cmd_sol (args , mc ):
7991 """Fetch SOL logs."""
92+ color = args .color
8093 if args .follow :
8194 # Fetch last 10 lines to start, then poll for new ones
8295 data = mc .get_sol_logs (args .machine_id , start_id = args .start_id ,
8396 limit = 10 , sort = 'desc' )
8497 lines = list (reversed (data .get ('lines' , [])))
8598 for entry in lines :
8699 ts = entry .get ('ts' , '' )
87- print (f"{ ts } { _sanitize (entry ['line' ])} " , end = '' )
100+ print (f"{ ts } { _sanitize (entry ['line' ], keep_color = color )} " , end = '' )
88101 last_id = data .get ('last_id' , 0 )
89102
90103 import time
@@ -95,7 +108,8 @@ def cmd_sol(args, mc):
95108 limit = args .limit )
96109 for entry in data .get ('lines' , []):
97110 ts = entry .get ('ts' , '' )
98- print (f"{ ts } { _sanitize (entry ['line' ])} " , end = '' , flush = True )
111+ print (f"{ ts } { _sanitize (entry ['line' ], keep_color = color )} " ,
112+ end = '' , flush = True )
99113 last_id = data .get ('last_id' , last_id )
100114 except KeyboardInterrupt :
101115 return 0
@@ -107,7 +121,7 @@ def cmd_sol(args, mc):
107121 return 0
108122 for entry in data .get ('lines' , []):
109123 ts = entry .get ('ts' , '' )
110- print (f"{ ts } { _sanitize (entry ['line' ])} " , end = '' )
124+ print (f"{ ts } { _sanitize (entry ['line' ], keep_color = color )} " , end = '' )
111125 last_id = data .get ('last_id' , 0 )
112126 print (f"last_id={ last_id } " , file = sys .stderr )
113127 return 0
@@ -214,6 +228,8 @@ def main(argv=None):
214228 help = 'follow output like tail -f' )
215229 p_sol .add_argument ('--interval' , type = float , default = 2 ,
216230 help = 'poll interval in seconds for -f (default: 2)' )
231+ p_sol .add_argument ('--color' , action = 'store_true' ,
232+ help = 'preserve ANSI color/formatting in output' )
217233
218234 p_reserve = sub .add_parser ('reserve' , help = 'reserve machines' )
219235 p_reserve .add_argument ('--machine-ids' , required = True ,
0 commit comments