44
55import asyncio
66import logging
7- import time
87from pathlib import Path
98from typing import Optional
109
1110import typer
1211import yaml
1312from rich .console import Console
13+ from rich .live import Live
1414from rich .logging import RichHandler
1515from rich .panel import Panel
16- from rich .table import Table
1716from rich .progress import Progress , SpinnerColumn , TextColumn
1817from rich .rule import Rule
19- from rich .live import Live
18+ from rich .table import Table
2019
2120from .. import __version__
2221from ..core .config import AuthConfig , ClientConfig
3433
3534def _format_bytes (num : int ) -> str :
3635 """Format bytes to human readable format"""
37- for unit in ['B' , 'KB' , 'MB' , 'GB' , 'TB' ]:
38- if abs (num ) < 1024.0 :
39- if unit == 'B' :
40- return f"{ num :.0f} { unit } "
41- return f"{ num :.1f} { unit } "
42- num /= 1024.0
43- return f"{ num :.1f} PB"
36+ size = float (num )
37+ for unit in ["B" , "KB" , "MB" , "GB" , "TB" ]:
38+ if abs (size ) < 1024.0 :
39+ if unit == "B" :
40+ return f"{ size :.0f} { unit } "
41+ return f"{ size :.1f} { unit } "
42+ size /= 1024.0
43+ return f"{ size :.1f} PB"
4444
4545
4646def setup_logging (level : str = "INFO" ) -> None :
@@ -49,7 +49,14 @@ def setup_logging(level: str = "INFO") -> None:
4949 level = level ,
5050 format = "%(message)s" ,
5151 datefmt = "[%X]" ,
52- handlers = [RichHandler (console = console , rich_tracebacks = True , show_time = False , show_path = False )],
52+ handlers = [
53+ RichHandler (
54+ console = console ,
55+ rich_tracebacks = True ,
56+ show_time = False ,
57+ show_path = False ,
58+ )
59+ ],
5360 )
5461
5562
@@ -213,14 +220,14 @@ def version() -> None:
213220def credits () -> None :
214221 """Show open source credits."""
215222 console .print ("\n [bold cyan]Open Source Credits[/bold cyan]\n " )
216-
223+
217224 table = Table (
218225 show_header = True ,
219226 header_style = "bold cyan" ,
220227 border_style = "dim" ,
221228 show_edge = False ,
222229 pad_edge = False ,
223- box = None
230+ box = None ,
224231 )
225232 table .add_column ("Package" , style = "cyan" )
226233 table .add_column ("License" , style = "green" )
@@ -250,26 +257,30 @@ async def _run_tunnel(
250257 """Run a single tunnel."""
251258 # Suppress client logging to preserve Rich output
252259 import logging as _logging
260+
253261 _logging .getLogger ("client" ).setLevel (_logging .ERROR )
254262 _logging .getLogger ("retunnel" ).setLevel (_logging .ERROR )
255263 _logging .getLogger ("aiohttp" ).setLevel (_logging .ERROR )
256264 _logging .getLogger ("asyncio" ).setLevel (_logging .ERROR )
257-
265+
258266 # Disable the default KeyboardInterrupt traceback
259267 import sys
268+
260269 sys .tracebacklimit = 0
261-
270+
262271 # Create client (server defaults to RETUNNEL_SERVER_ENDPOINT or localhost:6400)
263272 client = HighPerformanceClient (server , auth_token = token )
264273
265274 try :
266275 # Clean header
267276 console .clear ()
268277 console .print ()
269- console .print (f"[bold cyan]ReTunnel[/bold cyan] [dim]v{ __version__ } [/dim]" )
278+ console .print (
279+ f"[bold cyan]ReTunnel[/bold cyan] [dim]v{ __version__ } [/dim]"
280+ )
270281 console .print ("[dim]Secure tunnel service[/dim]" )
271282 console .print ()
272-
283+
273284 # Connection progress
274285 with Progress (
275286 SpinnerColumn (spinner_name = "dots" , style = "cyan" ),
@@ -279,23 +290,31 @@ async def _run_tunnel(
279290 ) as progress :
280291 task = progress .add_task ("Connecting" , total = None )
281292 await client .connect ()
282- progress .update (task , description = "[green]✓[/green] Connected successfully" )
293+ progress .update (
294+ task , description = "[green]✓[/green] Connected successfully"
295+ )
283296 await asyncio .sleep (0.3 )
284-
285- console .print (f "[green]✓[/green] Connected to tunnel service" )
297+
298+ console .print ("[green]✓[/green] Connected to tunnel service" )
286299 console .print (f"[dim]Client ID: { client .client_id } [/dim]" )
287300 console .print ()
288301
289302 # Request tunnel
290303 with Progress (
291304 SpinnerColumn (spinner_name = "dots" , style = "cyan" ),
292- TextColumn (f"[cyan]Creating { config .protocol .upper ()} tunnel...[/cyan]" ),
305+ TextColumn (
306+ "[cyan]Creating {protocol} tunnel...[/cyan]" .format (
307+ protocol = config .protocol .upper ()
308+ )
309+ ),
293310 console = console ,
294311 transient = True ,
295312 ) as progress :
296313 task = progress .add_task ("Creating" , total = None )
297314 tunnel = await client .request_tunnel (config )
298- progress .update (task , description = "[green]✓[/green] Tunnel created" )
315+ progress .update (
316+ task , description = "[green]✓[/green] Tunnel created"
317+ )
299318 await asyncio .sleep (0.3 )
300319
301320 # Display tunnel info cleanly
@@ -306,29 +325,29 @@ async def _run_tunnel(
306325 console .print ()
307326 console .print (f" [cyan]Protocol[/cyan] { tunnel .protocol .upper ()} " )
308327 console .print (f" [cyan]Local port[/cyan] { tunnel .config .local_port } " )
309- console .print (f " [cyan]Status[/cyan] [green]● Active[/green]" )
328+ console .print (" [cyan]Status[/cyan] [green]● Active[/green]" )
310329 console .print ()
311330 console .print (Rule (style = "dim" ))
312331 console .print ()
313332 console .print ("[dim]Press Ctrl+C to stop[/dim]" )
314333 console .print ()
315-
334+
316335 # Keep running and show stats using Live
317336 try :
318337 with Live (
319338 "[dim]↑ 0B ↓ 0B[/dim]" ,
320339 console = console ,
321340 refresh_per_second = 0.5 ,
322- transient = False
341+ transient = False ,
323342 ) as live :
324343 while True :
325344 stats = tunnel .get_stats ()
326- in_bytes = _format_bytes (stats [' bytes_in' ])
327- out_bytes = _format_bytes (stats [' bytes_out' ])
328-
345+ in_bytes = _format_bytes (stats [" bytes_in" ])
346+ out_bytes = _format_bytes (stats [" bytes_out" ])
347+
329348 # Update the live display
330349 live .update (f"[dim]↑ { in_bytes } ↓ { out_bytes } [/dim]" )
331-
350+
332351 await asyncio .sleep (2 )
333352 except KeyboardInterrupt :
334353 pass
@@ -345,7 +364,7 @@ async def _run_tunnel(
345364 raise typer .Exit (1 )
346365 finally :
347366 # Ensure client is closed
348- if ' client' in locals ():
367+ if " client" in locals ():
349368 await client .close ()
350369 # Give asyncio time to clean up
351370 await asyncio .sleep (0.1 )
0 commit comments