@@ -103,6 +103,7 @@ async def execute(
103103 command : List [str ],
104104 stdin : Optional [str ] = None ,
105105 directory : Optional [str ] = None ,
106+ timeout : Optional [int ] = None ,
106107 ) -> Dict [str , Any ]:
107108 """
108109 Execute a shell command with optional stdin input and working directory.
@@ -111,6 +112,7 @@ async def execute(
111112 command (List[str]): Command and its arguments
112113 stdin (Optional[str]): Input to be passed to the command via stdin
113114 directory (Optional[str]): Working directory for command execution
115+ timeout (Optional[int]): Timeout for command execution in seconds
114116
115117 Returns:
116118 Dict[str, Any]: Execution result containing stdout, stderr, status code, and execution time.
@@ -146,17 +148,36 @@ async def execute(
146148 cwd = directory , # Set working directory if specified
147149 )
148150
149- stdin_bytes = stdin .encode () if stdin else None
150- stdout , stderr = await process .communicate (input = stdin_bytes )
151+ try :
152+ stdin_bytes = stdin .encode () if stdin else None
153+ stdout , stderr = await asyncio .wait_for (
154+ process .communicate (input = stdin_bytes ), timeout = timeout
155+ )
156+
157+ return {
158+ "error" : None ,
159+ "stdout" : stdout .decode () if stdout else "" ,
160+ "stderr" : stderr .decode () if stderr else "" ,
161+ "status" : process .returncode ,
162+ "execution_time" : time .time () - start_time ,
163+ "directory" : directory ,
164+ }
165+
166+ except asyncio .TimeoutError :
167+ try :
168+ process .kill ()
169+ await process .wait ()
170+ except ProcessLookupError :
171+ pass
172+
173+ return {
174+ "error" : f"Command timed out after { timeout } seconds" ,
175+ "status" : - 1 ,
176+ "stdout" : "" ,
177+ "stderr" : f"Command timed out after { timeout } seconds" ,
178+ "execution_time" : time .time () - start_time ,
179+ }
151180
152- return {
153- "error" : None , # Set error field to None for success case
154- "stdout" : stdout .decode () if stdout else "" ,
155- "stderr" : stderr .decode () if stderr else "" ,
156- "status" : process .returncode ,
157- "execution_time" : time .time () - start_time ,
158- "directory" : directory , # Include working directory in response
159- }
160181 except FileNotFoundError :
161182 return {
162183 "error" : f"Command not found: { cleaned_command [0 ]} " ,
0 commit comments