@@ -121,6 +121,38 @@ def run_cmd(command: list[str]) -> str:
121121 raise CommandExecutionError (f"OS error while trying to run command '{ ' ' .join (command )} ': { e } " )
122122
123123
124+ def run_cmd_and_stream (command : list [str ]):
125+ '''
126+ Runs a command, streams its combined stdout/stderr, and raises an exception on failure.
127+ '''
128+ if DEBUG :
129+ print (f"Executing command: { ' ' .join (command )} " )
130+ try :
131+ process = subprocess .Popen (
132+ command ,
133+ stdout = subprocess .PIPE ,
134+ stderr = subprocess .STDOUT ,
135+ text = True ,
136+ bufsize = 1 ,
137+ universal_newlines = True
138+ )
139+
140+ if process .stdout :
141+ for line in iter (process .stdout .readline , '' ):
142+ print (line , end = '' )
143+ process .stdout .close ()
144+
145+ return_code = process .wait ()
146+
147+ if return_code != 0 :
148+ raise CommandExecutionError (f"Process failed with exit code { return_code } " )
149+
150+ except FileNotFoundError as e :
151+ raise ScriptNotFoundError (f"Script or command not found: { command [0 ]} . Original error: { e } " )
152+ except OSError as e :
153+ raise CommandExecutionError (f"OS error while trying to run command '{ ' ' .join (command )} ': { e } " )
154+
155+
124156def generate_password () -> str :
125157 '''
126158 Generates a secure, random alphanumeric password.
@@ -138,11 +170,11 @@ def generate_password() -> str:
138170# region Hysteria
139171
140172
141- def install_hysteria2 (port : int , sni : str ) -> str :
173+ def install_hysteria2 (port : int , sni : str ):
142174 '''
143- Installs Hysteria2 on the given port and uses the provided or default SNI value .
175+ Installs Hysteria2 and streams the output of the installation script .
144176 '''
145- return run_cmd (['bash' , Command .INSTALL_HYSTERIA2 .value , str (port ), sni ])
177+ run_cmd_and_stream (['bash' , Command .INSTALL_HYSTERIA2 .value , str (port ), sni ])
146178
147179
148180def uninstall_hysteria2 ():
@@ -388,7 +420,6 @@ def kick_users_by_name(usernames: list[str]):
388420 except subprocess .CalledProcessError as e :
389421 raise CommandExecutionError (f"Failed to execute kick user script: { e } " )
390422
391- # TODO: it's better to return json
392423def show_user_uri (username : str , qrcode : bool , ipv : int , all : bool , singbox : bool , normalsub : bool ) -> str | None :
393424 '''
394425 Displays the URI for a user, with options for QR code and other formats.
0 commit comments