1+ import os
12import subprocess
23import typer
34from rich import print
45from typing import Annotated
5- import os
66
7- app = typer .Typer (help = 'Surge - A DevOps CLI Tool For System Monitoring and Production Reliability' )
7+ app = typer .Typer (
8+ help = "Surge - A DevOps CLI Tool For System Monitoring and Production Reliability"
9+ )
810
911
1012def run_cmd (cmd : str ) -> str :
1113 """
1214 Helper function to abstract lengthy subprocess command implementation :D
1315 """
1416
15- return subprocess .run (cmd , shell = True , capture_output = True , text = True ).stdout .strip ()
17+ return subprocess .run (
18+ cmd , shell = True , capture_output = True , text = True
19+ ).stdout .strip ()
1620
1721
1822def get_load () -> tuple [float ] | int :
@@ -21,8 +25,8 @@ def get_load() -> tuple[float] | int:
2125 """
2226
2327 uptime = run_cmd ("uptime | awk -F'average:' '{print $2}'" )
24- averages = [float (x .replace (',' , '' )) for x in uptime .split ()]
25- cores = run_cmd (' nproc' )
28+ averages = [float (x .replace ("," , "" )) for x in uptime .split ()]
29+ cores = run_cmd (" nproc" )
2630
2731 return averages , cores
2832
@@ -31,7 +35,7 @@ def get_cpu() -> tuple[float]:
3135 """
3236 Uses top to find CPU utilization grouped by user, system, and idle percents.
3337 """
34- top = run_cmd ('top -bn1 | grep "Cpu(s)"' ).split (',' )
38+ top = run_cmd ('top -bn1 | grep "Cpu(s)"' ).split ("," )
3539 user = top [0 ].split ()[- 2 ]
3640 system = top [1 ].split ()[0 ]
3741 idle = top [3 ].split ()[0 ]
@@ -43,7 +47,7 @@ def get_memory() -> tuple[float]:
4347 """
4448 Returns remaining memory by group using free.
4549 """
46- free = run_cmd (' free -m | grep Mem' ).split ()
50+ free = run_cmd (" free -m | grep Mem" ).split ()
4751 total , used , free_mem = free [1 ], free [2 ], free [3 ]
4852
4953 return total , used , free_mem
@@ -53,7 +57,7 @@ def get_disk() -> tuple[float]:
5357 """
5458 Returns disk usage with df.
5559 """
56- df = run_cmd (' df -h / | tail -1' ).split ()
60+ df = run_cmd (" df -h / | tail -1" ).split ()
5761 size , used , available , percent = df [1 ], df [2 ], df [3 ], df [4 ]
5862
5963 return size , used , available , percent
@@ -66,13 +70,21 @@ def get_io() -> tuple[float]:
6670
6771@app .command ()
6872def monitor (
69- load : Annotated [bool , typer .Option ('-l' , '--load' , help = 'Show system load averages' )] = True ,
70- cpu : Annotated [bool , typer .Option ('-c' , '--cpu' , help = 'Show CPU usage' )] = True ,
71- ram : Annotated [bool , typer .Option ('-r' , '--ram' , help = 'Show RAM usage' )] = True ,
72- disk : Annotated [bool , typer .Option ('-d' , '--disk' , help = 'Show Disk usage' )] = True ,
73- io : Annotated [bool , typer .Option ('-o' , '--io' , help = 'Show Disk I/O statistics' )] = True ,
74- interval : Annotated [int , typer .Option ('-i' , '--interval' , help = 'Polling interval in seconds' )] = 5 ,
75- verbose : Annotated [bool , typer .Option ('-v' , '--verbose' , help = 'Show detailed system metrics' )] = False
73+ load : Annotated [
74+ bool , typer .Option ("-l" , "--load" , help = "Show system load averages" )
75+ ] = True ,
76+ cpu : Annotated [bool , typer .Option ("-c" , "--cpu" , help = "Show CPU usage" )] = True ,
77+ ram : Annotated [bool , typer .Option ("-r" , "--ram" , help = "Show RAM usage" )] = True ,
78+ disk : Annotated [bool , typer .Option ("-d" , "--disk" , help = "Show Disk usage" )] = True ,
79+ io : Annotated [
80+ bool , typer .Option ("-o" , "--io" , help = "Show Disk I/O statistics" )
81+ ] = True ,
82+ interval : Annotated [
83+ int , typer .Option ("-i" , "--interval" , help = "Polling interval in seconds" )
84+ ] = 5 ,
85+ verbose : Annotated [
86+ bool , typer .Option ("-v" , "--verbose" , help = "Show detailed system metrics" )
87+ ] = False ,
7688):
7789 """
7890 Summary of all system metrics, including utilization of CPU, Memory, Network, and I/O.
@@ -85,82 +97,95 @@ def monitor(
8597 # if verbose:
8698 # ...
8799
88-
89100 if load :
90101 averages , cores = get_load ()
91102
92- print (' \n [bold]System Load Averages[/bold]' )
93- print (' ---------------------' )
103+ print (" \n [bold]System Load Averages[/bold]" )
104+ print (" ---------------------" )
94105
95106 if not verbose :
96- print (f' Load avg (1m): { averages [0 ]} ' )
97- print (f' Load avg (5m): { averages [1 ]} ' )
98- print (f' Load avg (15m): { averages [2 ]} ' )
107+ print (f" Load avg (1m): { averages [0 ]} " )
108+ print (f" Load avg (5m): { averages [1 ]} " )
109+ print (f" Load avg (15m): { averages [2 ]} " )
99110 else :
100- print (f'Load avg (1m): { averages [0 ]:.2f} ({ averages [0 ] / cores :.3f} per CPU)' )
101- print (f'Load avg (5m): { averages [1 ]:.2f} ({ averages [1 ] / cores :.3f} per CPU)' )
102- print (f'Load avg (15m): { averages [2 ]:.2f} ({ averages [2 ] / cores :.3f} per CPU)' )
103-
111+ print (
112+ f"Load avg (1m): { averages [0 ]:.2f} ({ averages [0 ] / cores :.3f} per CPU)"
113+ )
114+ print (
115+ f"Load avg (5m): { averages [1 ]:.2f} ({ averages [1 ] / cores :.3f} per CPU)"
116+ )
117+ print (
118+ f"Load avg (15m): { averages [2 ]:.2f} ({ averages [2 ] / cores :.3f} per CPU)"
119+ )
120+
104121 if cpu :
105122 user , system , idle = get_cpu ()
106- print (' \n [bold]CPU Utilization[/bold]' )
107- print (' ---------------------' )
108- print (f' User: { user } % | System: { system } % | Idle: { idle } %' )
109-
123+ print (" \n [bold]CPU Utilization[/bold]" )
124+ print (" ---------------------" )
125+ print (f" User: { user } % | System: { system } % | Idle: { idle } %" )
126+
110127 if ram :
111128 total , used , free = get_memory ()
112- print (' \n [bold]Memory Usage (MB)[/bold]' )
113- print (' ---------------------' )
114- print (f' Total: { total } | Used: { used } | Free: { free } ' )
115-
129+ print (" \n [bold]Memory Usage (MB)[/bold]" )
130+ print (" ---------------------" )
131+ print (f" Total: { total } | Used: { used } | Free: { free } " )
132+
116133 if disk :
117134 size , used , available , percent = get_disk ()
118- print ('\n [bold]Disk Usage[/bold]' )
119- print ('---------------------' )
120- print (f'Size: { size } | Used: { used } | Available: { available } | Usage: { percent } ' )
135+ print ("\n [bold]Disk Usage[/bold]" )
136+ print ("---------------------" )
137+ print (
138+ f"Size: { size } | Used: { used } | Available: { available } | Usage: { percent } "
139+ )
121140
122141
123142@app .command ()
124143def network (
125- url : Annotated [str , typer .Argument (help = 'URL to test network/API metrics' )],
126- requests : Annotated [int , typer .Option ('-n' , '--count' , help = 'Number of requests to send' )] = 5
144+ url : Annotated [str , typer .Argument (help = "URL to test network/API metrics" )],
145+ requests : Annotated [
146+ int , typer .Option ("-n" , "--count" , help = "Number of requests to send" )
147+ ] = 5 ,
127148):
128149 """
129150 Run basic network/API tests with a number of requests.
130151 """
131- print (f' Testing network connection to { url } with { requests } requests.' )
152+ print (f" Testing network connection to { url } with { requests } requests." )
132153 # TODO: Add http requests or use curl through subprocess
133154
134155
135156@app .command ()
136157def ai (
137- format : Annotated [str , typer .Option ("--format" , '-f' ,help = 'Data format: raw/structured/hybrid' )] = "hybrid" ,
138- verbosity : Annotated [str , typer .Option ('--verbosity' , '-v' , help = "Output detail: concise/normal/hybrid" )] = 'normal' ,
139- auto_fix : Annotated [bool , typer .Option ("--auto-fix" , help = "Auto-execute safe fixes" )] = False ,
158+ format : Annotated [
159+ str , typer .Option ("--format" , "-f" , help = "Data format: raw/structured/hybrid" )
160+ ] = "hybrid" ,
161+ verbosity : Annotated [
162+ str ,
163+ typer .Option ("--verbosity" , "-v" , help = "Output detail: concise/normal/hybrid" ),
164+ ] = "normal" ,
165+ auto_fix : Annotated [
166+ bool , typer .Option ("--auto-fix" , help = "Auto-execute safe fixes" )
167+ ] = False ,
140168):
141169 """
142170 Using Gemini 2.5 Flash for now for simple AI suggestions and reading through machine metrics
143171 giving suggest fixes upon user confirmations
144172 """
145173
146- if not os .getenv (' GEMINI_API_KEY' ):
147- print (' [red]Error: GEMINI_API_KEY is empty[/red]' )
174+ if not os .getenv (" GEMINI_API_KEY" ):
175+ print (" [red]Error: GEMINI_API_KEY is empty[/red]" )
148176 print ('Set it with: export GEMINI_API_KEY="your api key"' )
149177 return
150178
151179 try :
152180 from ai .ai_monitor import run_ai_monitor
153-
154- run_ai_monitor (
155- data_format = format ,
156- verbosity = verbosity ,
157- auto_fix = auto_fix
158- )
181+
182+ run_ai_monitor (data_format = format , verbosity = verbosity , auto_fix = auto_fix )
159183 except ImportError :
160- print (' [red]AI packages not installed[/red]' )
161- print (' Run: pip install langchain langchain-google-genai rich' )
184+ print (" [red]AI packages not installed[/red]" )
185+ print (" Run: pip install langchain langchain-google-genai rich" )
162186 except Exception as e :
163- print (f'[red]Error: { str (e )} [/red]' )
187+ print (f"[red]Error: { str (e )} [/red]" )
188+
164189
165190if __name__ == "__main__" :
166- app ()
191+ app ()
0 commit comments