From dd8385abf123b22da0a24a2a3f412a5c299fdad7 Mon Sep 17 00:00:00 2001 From: Harry Date: Sat, 14 Feb 2026 16:01:12 +0800 Subject: [PATCH] feat: centralize command execution timeout constant for sandbox providers --- api/core/virtual_environment/constants.py | 10 +++++++ .../providers/e2b_sandbox.py | 2 ++ .../providers/ssh_sandbox.py | 26 +++++++++---------- 3 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 api/core/virtual_environment/constants.py diff --git a/api/core/virtual_environment/constants.py b/api/core/virtual_environment/constants.py new file mode 100644 index 0000000000..11662bb817 --- /dev/null +++ b/api/core/virtual_environment/constants.py @@ -0,0 +1,10 @@ +""" +Constants for virtual environment providers. + +Centralizes timeout and other configuration values used across different sandbox providers +(E2B, SSH, Docker) to ensure consistency and ease of maintenance. +""" + +# Command execution timeout in seconds (5 hours) +# Used by providers to limit how long a single command can run +COMMAND_EXECUTION_TIMEOUT_SECONDS = 5 * 60 * 60 # 18000 seconds diff --git a/api/core/virtual_environment/providers/e2b_sandbox.py b/api/core/virtual_environment/providers/e2b_sandbox.py index a298c35c70..31d68fb77f 100644 --- a/api/core/virtual_environment/providers/e2b_sandbox.py +++ b/api/core/virtual_environment/providers/e2b_sandbox.py @@ -30,6 +30,7 @@ from core.virtual_environment.channel.transport import ( TransportReadCloser, TransportWriteCloser, ) +from core.virtual_environment.constants import COMMAND_EXECUTION_TIMEOUT_SECONDS logger = logging.getLogger(__name__) @@ -292,6 +293,7 @@ class E2BEnvironment(VirtualEnvironment): cwd=cwd, on_stdout=lambda data: stdout_stream_write_handler.write(data.encode()), on_stderr=lambda data: stderr_stream_write_handler.write(data.encode()), + timeout=COMMAND_EXECUTION_TIMEOUT_SECONDS, ) except Exception as e: # Capture exceptions and write to stderr stream so they can be retrieved via CommandFuture diff --git a/api/core/virtual_environment/providers/ssh_sandbox.py b/api/core/virtual_environment/providers/ssh_sandbox.py index a4c63f6110..f43f218c69 100644 --- a/api/core/virtual_environment/providers/ssh_sandbox.py +++ b/api/core/virtual_environment/providers/ssh_sandbox.py @@ -27,6 +27,7 @@ from core.virtual_environment.__base.virtual_environment import VirtualEnvironme from core.virtual_environment.channel.exec import TransportEOFError from core.virtual_environment.channel.queue_transport import QueueTransportReadCloser from core.virtual_environment.channel.transport import TransportWriteCloser +from core.virtual_environment.constants import COMMAND_EXECUTION_TIMEOUT_SECONDS logger = logging.getLogger(__name__) @@ -55,9 +56,8 @@ class SSHSandboxEnvironment(VirtualEnvironment): _DEFAULT_SSH_HOST = "agentbox" _DEFAULT_SSH_PORT = 22 _DEFAULT_BASE_WORKING_PATH = "/workspace/sandboxes" - _DEFAULT_SSH_CONNECT_TIMEOUT_SECONDS = 10 - _DEFAULT_SSH_OPERATION_TIMEOUT_SECONDS = 30 - _DEFAULT_COMMAND_MAX_RUNTIME_SECONDS = 60 * 60 + _SSH_CONNECT_TIMEOUT_SECONDS = 10 + _SSH_OPERATION_TIMEOUT_SECONDS = 30 _COMMAND_TIMEOUT_EXIT_CODE = 124 class OptionsKey(StrEnum): @@ -150,7 +150,7 @@ class SSHSandboxEnvironment(VirtualEnvironment): raise RuntimeError("SSH transport is not available") channel = transport.open_session() - channel.settimeout(self._DEFAULT_SSH_OPERATION_TIMEOUT_SECONDS) + channel.settimeout(self._SSH_OPERATION_TIMEOUT_SECONDS) channel.set_combine_stderr(False) execution_command = self._build_exec_command(command, environments, cwd) @@ -166,7 +166,7 @@ class SSHSandboxEnvironment(VirtualEnvironment): threading.Thread( target=self._consume_channel_output, - args=(pid, channel, stdout_transport, stderr_transport, self._DEFAULT_COMMAND_MAX_RUNTIME_SECONDS), + args=(pid, channel, stdout_transport, stderr_transport, COMMAND_EXECUTION_TIMEOUT_SECONDS), daemon=True, ).start() @@ -274,9 +274,9 @@ class SSHSandboxEnvironment(VirtualEnvironment): password=password, look_for_keys=False, allow_agent=False, - timeout=cls._DEFAULT_SSH_CONNECT_TIMEOUT_SECONDS, - banner_timeout=cls._DEFAULT_SSH_CONNECT_TIMEOUT_SECONDS, - auth_timeout=cls._DEFAULT_SSH_CONNECT_TIMEOUT_SECONDS, + timeout=cls._SSH_CONNECT_TIMEOUT_SECONDS, + banner_timeout=cls._SSH_CONNECT_TIMEOUT_SECONDS, + auth_timeout=cls._SSH_CONNECT_TIMEOUT_SECONDS, ) transport = client.get_transport() if transport is not None: @@ -361,15 +361,15 @@ class SSHSandboxEnvironment(VirtualEnvironment): @classmethod def _run_command(cls, client: Any, command: str) -> bytes: - _, stdout, stderr = client.exec_command(command, timeout=cls._DEFAULT_SSH_OPERATION_TIMEOUT_SECONDS) - stdout.channel.settimeout(cls._DEFAULT_SSH_OPERATION_TIMEOUT_SECONDS) + _, stdout, stderr = client.exec_command(command, timeout=cls._SSH_OPERATION_TIMEOUT_SECONDS) + stdout.channel.settimeout(cls._SSH_OPERATION_TIMEOUT_SECONDS) - deadline = time.monotonic() + cls._DEFAULT_COMMAND_MAX_RUNTIME_SECONDS + deadline = time.monotonic() + COMMAND_EXECUTION_TIMEOUT_SECONDS while not stdout.channel.exit_status_ready(): if time.monotonic() >= deadline: with contextlib.suppress(Exception): stdout.channel.close() - raise TimeoutError(f"SSH command timed out after {cls._DEFAULT_COMMAND_MAX_RUNTIME_SECONDS}s") + raise TimeoutError(f"SSH command timed out after {COMMAND_EXECUTION_TIMEOUT_SECONDS}s") time.sleep(0.05) exit_code = stdout.channel.recv_exit_status() @@ -428,7 +428,7 @@ class SSHSandboxEnvironment(VirtualEnvironment): def _set_sftp_operation_timeout(self, sftp: Any) -> None: with contextlib.suppress(Exception): - sftp.get_channel().settimeout(self._DEFAULT_SSH_OPERATION_TIMEOUT_SECONDS) + sftp.get_channel().settimeout(self._SSH_OPERATION_TIMEOUT_SECONDS) @staticmethod def _parse_arch(raw_arch: str) -> Arch: