Compare commits

...

5 Commits

Author SHA1 Message Date
openhands
853bb51b1e fix: make networking changes optional via environment variables
- Add OPENHANDS_HEALTH_CHECK_HOST env var (default: localhost, can be set to 127.0.0.1)
- Add OPENHANDS_DISABLE_DOCKER_HOST_REWRITE env var to disable localhost->host.docker.internal
- Keep STATUS_SLEEPING fix for process status detection
- Revert nginx proxy changes in live_status_app_conversation_service.py
- Remove unnecessary start.sh workaround
- Keep libtmux API fixes (session.active_pane, session.kill())

Co-authored-by: openhands <openhands@all-hands.dev>
2026-02-18 21:03:34 +00:00
Robert Brennan
aad67d0e1f Route websocket through nginx: set conversation_url to empty for local mode, proxy /sockets/ to agent server on port 8000 2026-02-18 20:12:23 +00:00
Robert Brennan
42ce372684 Add nginx config and start script for websocket proxying in local mode 2026-02-18 19:33:07 +00:00
Robert Brennan
2c62e3793e fix: rewrite agent server URLs to go through nginx reverse proxy
In local/process mode, the frontend receives the agent server URL
(e.g. http://127.0.0.1:8001) which is unreachable from the browser
when behind a reverse proxy. This rewrites those URLs to relative
paths (/agent/{port}/...) that nginx can proxy to the correct
local agent server.
2026-02-18 19:19:36 +00:00
Robert Brennan
8dfa8e0f05 fix: local runtime compatibility with modern libtmux and non-Docker containers
- Update deprecated libtmux API calls: session.attached_pane ->
  session.active_pane, session.kill_session() -> session.kill()
- Use 127.0.0.1 instead of localhost in ProcessSandboxService health
  checks to avoid IPv6 resolution failures and remove dependency on
  replace_localhost_hostname_for_docker which rewrites URLs to
  host.docker.internal in container environments that aren't Docker
2026-02-18 17:27:26 +00:00
2 changed files with 44 additions and 7 deletions

View File

@@ -44,6 +44,37 @@ from openhands.app_server.utils.docker_utils import (
_logger = logging.getLogger(__name__)
def _get_health_check_host() -> str:
"""Get the health check host from environment variable.
Defaults to 'localhost', but can be set to '127.0.0.1' via
OPENHANDS_HEALTH_CHECK_HOST to avoid IPv6 resolution issues.
"""
return os.environ.get('OPENHANDS_HEALTH_CHECK_HOST', 'localhost')
def _is_docker_host_rewrite_disabled() -> bool:
"""Check if Docker hostname rewrite is disabled.
Set OPENHANDS_DISABLE_DOCKER_HOST_REWRITE to '1', 'true', or 'yes'
to disable replacing 'localhost' with 'host.docker.internal' in
non-Docker container environments (e.g., gVisor, LXC).
"""
value = os.environ.get('OPENHANDS_DISABLE_DOCKER_HOST_REWRITE', '').lower()
return value in ('1', 'true', 'yes')
def _maybe_replace_localhost_for_docker(url: str) -> str:
"""Conditionally replace localhost hostname for Docker.
This wrapper allows disabling the Docker hostname rewrite via
OPENHANDS_DISABLE_DOCKER_HOST_REWRITE for non-Docker containers.
"""
if _is_docker_host_rewrite_disabled():
return url
return replace_localhost_hostname_for_docker(url)
class ProcessInfo(BaseModel):
"""Information about a running process."""
@@ -159,10 +190,11 @@ class ProcessSandboxService(SandboxService):
async def _wait_for_server_ready(self, port: int, timeout: int = 30) -> bool:
"""Wait for the agent server to be ready."""
start_time = time.time()
health_check_host = _get_health_check_host()
while time.time() - start_time < timeout:
try:
url = replace_localhost_hostname_for_docker(
f'http://localhost:{port}/alive'
url = _maybe_replace_localhost_for_docker(
f'http://{health_check_host}:{port}/alive'
)
response = await self.httpx_client.get(url, timeout=5.0)
if response.status_code == 200:
@@ -180,7 +212,11 @@ class ProcessSandboxService(SandboxService):
process = psutil.Process(process_info.pid)
if process.is_running():
status = process.status()
if status == psutil.STATUS_RUNNING:
if status in (
psutil.STATUS_RUNNING,
psutil.STATUS_SLEEPING,
psutil.STATUS_DISK_SLEEP,
):
return SandboxStatus.RUNNING
elif status == psutil.STATUS_STOPPED:
return SandboxStatus.PAUSED
@@ -199,12 +235,13 @@ class ProcessSandboxService(SandboxService):
exposed_urls = None
session_api_key = None
health_check_host = _get_health_check_host()
if status == SandboxStatus.RUNNING:
# Check if server is actually responding
try:
url = replace_localhost_hostname_for_docker(
f'http://localhost:{process_info.port}{self.health_check_path}'
url = _maybe_replace_localhost_for_docker(
f'http://{health_check_host}:{process_info.port}{self.health_check_path}'
)
response = await self.httpx_client.get(url, timeout=5.0)
if response.status_code == 200:

View File

@@ -115,10 +115,10 @@ def check_dependencies(code_repo_path: str, check_browser: bool) -> None:
session = server.new_session(session_name='test-session')
except Exception:
raise ValueError('tmux is not properly installed or available on the path.')
pane = session.attached_pane
pane = session.active_pane
pane.send_keys('echo "test"')
pane_output = '\n'.join(pane.cmd('capture-pane', '-p').stdout)
session.kill_session()
session.kill()
if 'test' not in pane_output:
raise ValueError('libtmux is not properly installed. ' + ERROR_MESSAGE)