From fccc6f31961ef2792e1d08a1659b5ecaf4d44a37 Mon Sep 17 00:00:00 2001 From: Tim O'Farrell Date: Tue, 28 Oct 2025 14:24:54 -0600 Subject: [PATCH] Fix permissions issue in docker Sandbox (#11549) --- enterprise/poetry.lock | 30 ++++++++--------- .../app_conversation_service.py | 5 ++- .../git_app_conversation_service.py | 32 ++++++++++--------- .../live_status_app_conversation_service.py | 8 ++--- .../sandbox/docker_sandbox_spec_service.py | 6 ++-- .../sandbox/sandbox_spec_service.py | 2 +- openhands/app_server/utils/encryption_key.py | 2 +- poetry.lock | 25 +++++++-------- pyproject.toml | 6 ++-- ...st_docker_sandbox_spec_service_injector.py | 2 +- 10 files changed, 58 insertions(+), 60 deletions(-) diff --git a/enterprise/poetry.lock b/enterprise/poetry.lock index d94cba5ae3..072988f79f 100644 --- a/enterprise/poetry.lock +++ b/enterprise/poetry.lock @@ -5737,7 +5737,7 @@ llama = ["llama-index (>=0.12.29,<0.13.0)", "llama-index-core (>=0.12.29,<0.13.0 [[package]] name = "openhands-agent-server" -version = "1.0.0a3" +version = "1.0.0a4" description = "OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent" optional = false python-versions = ">=3.12" @@ -5758,9 +5758,9 @@ wsproto = ">=1.2.0" [package.source] type = "git" -url = "https://github.com/All-Hands-AI/agent-sdk.git" -reference = "8d8134ca5a87cc3e90e3ff968327a7f4c961e22e" -resolved_reference = "8d8134ca5a87cc3e90e3ff968327a7f4c961e22e" +url = "https://github.com/OpenHands/agent-sdk.git" +reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" +resolved_reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" subdirectory = "openhands-agent-server" [[package]] @@ -5805,9 +5805,9 @@ memory-profiler = "^0.61.0" numpy = "*" openai = "1.99.9" openhands-aci = "0.3.2" -openhands-agent-server = {git = "https://github.com/All-Hands-AI/agent-sdk.git", rev = "8d8134ca5a87cc3e90e3ff968327a7f4c961e22e", subdirectory = "openhands-agent-server"} -openhands-sdk = {git = "https://github.com/All-Hands-AI/agent-sdk.git", rev = "8d8134ca5a87cc3e90e3ff968327a7f4c961e22e", subdirectory = "openhands-sdk"} -openhands-tools = {git = "https://github.com/All-Hands-AI/agent-sdk.git", rev = "8d8134ca5a87cc3e90e3ff968327a7f4c961e22e", subdirectory = "openhands-tools"} +openhands-agent-server = {git = "https://github.com/OpenHands/agent-sdk.git", rev = "ce0a71af55dfce101f7419fbdb0116178f01e109", subdirectory = "openhands-agent-server"} +openhands-sdk = {git = "https://github.com/OpenHands/agent-sdk.git", rev = "ce0a71af55dfce101f7419fbdb0116178f01e109", subdirectory = "openhands-sdk"} +openhands-tools = {git = "https://github.com/OpenHands/agent-sdk.git", rev = "ce0a71af55dfce101f7419fbdb0116178f01e109", subdirectory = "openhands-tools"} opentelemetry-api = "^1.33.1" opentelemetry-exporter-otlp-proto-grpc = "^1.33.1" pathspec = "^0.12.1" @@ -5863,7 +5863,7 @@ url = ".." [[package]] name = "openhands-sdk" -version = "1.0.0a3" +version = "1.0.0a4" description = "OpenHands SDK - Core functionality for building AI agents" optional = false python-versions = ">=3.12" @@ -5886,14 +5886,14 @@ boto3 = ["boto3 (>=1.35.0)"] [package.source] type = "git" -url = "https://github.com/All-Hands-AI/agent-sdk.git" -reference = "8d8134ca5a87cc3e90e3ff968327a7f4c961e22e" -resolved_reference = "8d8134ca5a87cc3e90e3ff968327a7f4c961e22e" +url = "https://github.com/OpenHands/agent-sdk.git" +reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" +resolved_reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" subdirectory = "openhands-sdk" [[package]] name = "openhands-tools" -version = "1.0.0a3" +version = "1.0.0a4" description = "OpenHands Tools - Runtime tools for AI agents" optional = false python-versions = ">=3.12" @@ -5913,9 +5913,9 @@ pydantic = ">=2.11.7" [package.source] type = "git" -url = "https://github.com/All-Hands-AI/agent-sdk.git" -reference = "8d8134ca5a87cc3e90e3ff968327a7f4c961e22e" -resolved_reference = "8d8134ca5a87cc3e90e3ff968327a7f4c961e22e" +url = "https://github.com/OpenHands/agent-sdk.git" +reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" +resolved_reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" subdirectory = "openhands-tools" [[package]] diff --git a/openhands/app_server/app_conversation/app_conversation_service.py b/openhands/app_server/app_conversation/app_conversation_service.py index 2cff627aeb..4051ae1ba2 100644 --- a/openhands/app_server/app_conversation/app_conversation_service.py +++ b/openhands/app_server/app_conversation/app_conversation_service.py @@ -12,8 +12,8 @@ from openhands.app_server.app_conversation.app_conversation_models import ( AppConversationStartTask, ) from openhands.app_server.services.injector import Injector -from openhands.sdk import Workspace from openhands.sdk.utils.models import DiscriminatedUnionMixin +from openhands.sdk.workspace.remote.async_remote_workspace import AsyncRemoteWorkspace class AppConversationService(ABC): @@ -90,8 +90,7 @@ class AppConversationService(ABC): async def run_setup_scripts( self, task: AppConversationStartTask, - workspace: Workspace, - working_dir: str, + workspace: AsyncRemoteWorkspace, ) -> AsyncGenerator[AppConversationStartTask, None]: """Run the setup scripts for the project and yield status updates""" yield task diff --git a/openhands/app_server/app_conversation/git_app_conversation_service.py b/openhands/app_server/app_conversation/git_app_conversation_service.py index 4ea9099163..26049ad9b1 100644 --- a/openhands/app_server/app_conversation/git_app_conversation_service.py +++ b/openhands/app_server/app_conversation/git_app_conversation_service.py @@ -36,35 +36,36 @@ class GitAppConversationService(AppConversationService, ABC): self, task: AppConversationStartTask, workspace: AsyncRemoteWorkspace, - working_dir: str, ) -> AsyncGenerator[AppConversationStartTask, None]: task.status = AppConversationStartTaskStatus.PREPARING_REPOSITORY yield task - await self.clone_or_init_git_repo(task, workspace, working_dir) + await self.clone_or_init_git_repo(task, workspace) task.status = AppConversationStartTaskStatus.RUNNING_SETUP_SCRIPT yield task - await self.maybe_run_setup_script(workspace, working_dir) + await self.maybe_run_setup_script(workspace) task.status = AppConversationStartTaskStatus.SETTING_UP_GIT_HOOKS yield task - await self.maybe_setup_git_hooks(workspace, working_dir) + await self.maybe_setup_git_hooks(workspace) async def clone_or_init_git_repo( self, task: AppConversationStartTask, workspace: AsyncRemoteWorkspace, - working_dir: str, ): request = task.request if not request.selected_repository: if self.init_git_in_empty_workspace: _logger.debug('Initializing a new git repository in the workspace.') - await workspace.execute_command( - 'git init && git config --global --add safe.directory ' - + working_dir + cmd = ( + 'git init && git config --global ' + f'--add safe.directory {workspace.working_dir}' ) + result = await workspace.execute_command(cmd, workspace.working_dir) + if result.exit_code: + _logger.warning(f'Git init failed: {result.stderr}') else: _logger.info('Not initializing a new git repository.') return @@ -79,7 +80,8 @@ class GitAppConversationService(AppConversationService, ABC): # Clone the repo - this is the slow part! clone_command = f'git clone {remote_repo_url} {dir_name}' - await workspace.execute_command(clone_command, working_dir) + result = await workspace.execute_command(clone_command, workspace.working_dir) + print(result) # Checkout the appropriate branch if request.selected_branch: @@ -89,15 +91,14 @@ class GitAppConversationService(AppConversationService, ABC): random_str = base62.encodebytes(os.urandom(16)) openhands_workspace_branch = f'openhands-workspace-{random_str}' checkout_command = f'git checkout -b {openhands_workspace_branch}' - await workspace.execute_command(checkout_command, working_dir) + await workspace.execute_command(checkout_command, workspace.working_dir) async def maybe_run_setup_script( self, workspace: AsyncRemoteWorkspace, - working_dir: str, ): """Run .openhands/setup.sh if it exists in the workspace or repository.""" - setup_script = working_dir + '/.openhands/setup.sh' + setup_script = workspace.working_dir + '/.openhands/setup.sh' await workspace.execute_command( f'chmod +x {setup_script} && source {setup_script}', timeout=600 @@ -111,11 +112,10 @@ class GitAppConversationService(AppConversationService, ABC): async def maybe_setup_git_hooks( self, workspace: AsyncRemoteWorkspace, - working_dir: str, ): """Set up git hooks if .openhands/pre-commit.sh exists in the workspace or repository.""" command = 'mkdir -p .git/hooks && chmod +x .openhands/pre-commit.sh' - result = await workspace.execute_command(command, working_dir) + result = await workspace.execute_command(command, workspace.working_dir) if result.exit_code: return @@ -131,7 +131,9 @@ class GitAppConversationService(AppConversationService, ABC): f'mv {PRE_COMMIT_HOOK} {PRE_COMMIT_LOCAL} &&' f'chmod +x {PRE_COMMIT_LOCAL}' ) - result = await workspace.execute_command(command, working_dir) + result = await workspace.execute_command( + command, workspace.working_dir + ) if result.exit_code != 0: _logger.error( f'Failed to preserve existing pre-commit hook: {result.stderr}', diff --git a/openhands/app_server/app_conversation/live_status_app_conversation_service.py b/openhands/app_server/app_conversation/live_status_app_conversation_service.py index 3c8ee7203c..c58560ad5c 100644 --- a/openhands/app_server/app_conversation/live_status_app_conversation_service.py +++ b/openhands/app_server/app_conversation/live_status_app_conversation_service.py @@ -181,11 +181,11 @@ class LiveStatusAppConversationService(GitAppConversationService): # Run setup scripts workspace = AsyncRemoteWorkspace( - host=agent_server_url, api_key=sandbox.session_api_key + host=agent_server_url, + api_key=sandbox.session_api_key, + working_dir=sandbox_spec.working_dir, ) - async for updated_task in self.run_setup_scripts( - task, workspace, sandbox_spec.working_dir - ): + async for updated_task in self.run_setup_scripts(task, workspace): yield updated_task # Build the start request diff --git a/openhands/app_server/sandbox/docker_sandbox_spec_service.py b/openhands/app_server/sandbox/docker_sandbox_spec_service.py index dcf118911b..cd42cbfe70 100644 --- a/openhands/app_server/sandbox/docker_sandbox_spec_service.py +++ b/openhands/app_server/sandbox/docker_sandbox_spec_service.py @@ -40,10 +40,10 @@ def get_default_sandbox_specs(): 'OPENVSCODE_SERVER_ROOT': '/openhands/.openvscode-server', 'OH_ENABLE_VNC': '0', 'LOG_JSON': 'true', - 'OH_CONVERSATIONS_PATH': '/home/openhands/conversations', - 'OH_BASH_EVENTS_DIR': '/home/openhands/bash_events', + 'OH_CONVERSATIONS_PATH': '/workspace/conversations', + 'OH_BASH_EVENTS_DIR': '/workspace/bash_events', }, - working_dir='/home/openhands/workspace', + working_dir='/workspace/project', ) ] diff --git a/openhands/app_server/sandbox/sandbox_spec_service.py b/openhands/app_server/sandbox/sandbox_spec_service.py index f11be9fad3..1c47818336 100644 --- a/openhands/app_server/sandbox/sandbox_spec_service.py +++ b/openhands/app_server/sandbox/sandbox_spec_service.py @@ -11,7 +11,7 @@ from openhands.sdk.utils.models import DiscriminatedUnionMixin # The version of the agent server to use for deployments. # Typically this will be the same as the values from the pyproject.toml -AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:2381484-python' +AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:ce0a71a-python' class SandboxSpecService(ABC): diff --git a/openhands/app_server/utils/encryption_key.py b/openhands/app_server/utils/encryption_key.py index 367d4d8a39..5815bce20e 100644 --- a/openhands/app_server/utils/encryption_key.py +++ b/openhands/app_server/utils/encryption_key.py @@ -21,7 +21,7 @@ class EncryptionKey(BaseModel): @field_serializer('key') def serialize_key(self, key: SecretStr, info: Any): """Conditionally serialize the key based on context.""" - if info.context and info.context.get('reveal_secrets'): + if info.context and info.context.get('expose_secrets'): return key.get_secret_value() return str(key) # Returns '**********' by default diff --git a/poetry.lock b/poetry.lock index b8169425f1..4f3bd5ad3a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aiofiles" @@ -5711,11 +5711,8 @@ files = [ {file = "lxml-5.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7ce1a171ec325192c6a636b64c94418e71a1964f56d002cc28122fceff0b6121"}, {file = "lxml-5.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:795f61bcaf8770e1b37eec24edf9771b307df3af74d1d6f27d812e15a9ff3872"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29f451a4b614a7b5b6c2e043d7b64a15bd8304d7e767055e8ab68387a8cacf4e"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:891f7f991a68d20c75cb13c5c9142b2a3f9eb161f1f12a9489c82172d1f133c0"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4aa412a82e460571fad592d0f93ce9935a20090029ba08eca05c614f99b0cc92"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:ac7ba71f9561cd7d7b55e1ea5511543c0282e2b6450f122672a2694621d63b7e"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:c5d32f5284012deaccd37da1e2cd42f081feaa76981f0eaa474351b68df813c5"}, - {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:ce31158630a6ac85bddd6b830cffd46085ff90498b397bd0a259f59d27a12188"}, {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:31e63621e073e04697c1b2d23fcb89991790eef370ec37ce4d5d469f40924ed6"}, {file = "lxml-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:be2ba4c3c5b7900246a8f866580700ef0d538f2ca32535e991027bdaba944063"}, {file = "lxml-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:09846782b1ef650b321484ad429217f5154da4d6e786636c38e434fa32e94e49"}, @@ -7275,7 +7272,7 @@ llama = ["llama-index (>=0.12.29,<0.13.0)", "llama-index-core (>=0.12.29,<0.13.0 [[package]] name = "openhands-agent-server" -version = "1.0.0a3" +version = "1.0.0a4" description = "OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent" optional = false python-versions = ">=3.12" @@ -7297,13 +7294,13 @@ wsproto = ">=1.2.0" [package.source] type = "git" url = "https://github.com/OpenHands/agent-sdk.git" -reference = "93b481c50fab2bb45e6065606219155119d35656" -resolved_reference = "93b481c50fab2bb45e6065606219155119d35656" +reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" +resolved_reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" subdirectory = "openhands-agent-server" [[package]] name = "openhands-sdk" -version = "1.0.0a3" +version = "1.0.0a4" description = "OpenHands SDK - Core functionality for building AI agents" optional = false python-versions = ">=3.12" @@ -7327,13 +7324,13 @@ boto3 = ["boto3 (>=1.35.0)"] [package.source] type = "git" url = "https://github.com/OpenHands/agent-sdk.git" -reference = "93b481c50fab2bb45e6065606219155119d35656" -resolved_reference = "93b481c50fab2bb45e6065606219155119d35656" +reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" +resolved_reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" subdirectory = "openhands-sdk" [[package]] name = "openhands-tools" -version = "1.0.0a3" +version = "1.0.0a4" description = "OpenHands Tools - Runtime tools for AI agents" optional = false python-versions = ">=3.12" @@ -7354,8 +7351,8 @@ pydantic = ">=2.11.7" [package.source] type = "git" url = "https://github.com/OpenHands/agent-sdk.git" -reference = "93b481c50fab2bb45e6065606219155119d35656" -resolved_reference = "93b481c50fab2bb45e6065606219155119d35656" +reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" +resolved_reference = "ce0a71af55dfce101f7419fbdb0116178f01e109" subdirectory = "openhands-tools" [[package]] @@ -16524,4 +16521,4 @@ third-party-runtimes = ["daytona", "e2b-code-interpreter", "modal", "runloop-api [metadata] lock-version = "2.1" python-versions = "^3.12,<3.14" -content-hash = "b8620f03973119b97edf2ce1d44e4d8706cb2ecf155710bc8e2094daa766d139" +content-hash = "aed9fa5020f1fdda19cf8191ac75021f2617e10e49757bcec23586b2392fd596" diff --git a/pyproject.toml b/pyproject.toml index 31f5a4e880..48bfb51731 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,9 +113,9 @@ e2b-code-interpreter = { version = "^2.0.0", optional = true } pybase62 = "^1.0.0" # V1 dependencies -openhands-agent-server = { git = "https://github.com/OpenHands/agent-sdk.git", subdirectory = "openhands-agent-server", rev = "93b481c50fab2bb45e6065606219155119d35656" } -openhands-sdk = { git = "https://github.com/OpenHands/agent-sdk.git", subdirectory = "openhands-sdk", rev = "93b481c50fab2bb45e6065606219155119d35656" } -openhands-tools = { git = "https://github.com/OpenHands/agent-sdk.git", subdirectory = "openhands-tools", rev = "93b481c50fab2bb45e6065606219155119d35656" } +openhands-agent-server = { git = "https://github.com/OpenHands/agent-sdk.git", subdirectory = "openhands-agent-server", rev = "ce0a71af55dfce101f7419fbdb0116178f01e109" } +openhands-sdk = { git = "https://github.com/OpenHands/agent-sdk.git", subdirectory = "openhands-sdk", rev = "ce0a71af55dfce101f7419fbdb0116178f01e109" } +openhands-tools = { git = "https://github.com/OpenHands/agent-sdk.git", subdirectory = "openhands-tools", rev = "ce0a71af55dfce101f7419fbdb0116178f01e109" } python-jose = { version = ">=3.3", extras = [ "cryptography" ] } sqlalchemy = { extras = [ "asyncio" ], version = "^2.0.40" } pg8000 = "^1.31.5" diff --git a/tests/unit/app_server/test_docker_sandbox_spec_service_injector.py b/tests/unit/app_server/test_docker_sandbox_spec_service_injector.py index 21b991ffe8..1df987c56f 100644 --- a/tests/unit/app_server/test_docker_sandbox_spec_service_injector.py +++ b/tests/unit/app_server/test_docker_sandbox_spec_service_injector.py @@ -365,7 +365,7 @@ class TestDockerSandboxSpecServiceInjector: assert 'OPENVSCODE_SERVER_ROOT' in specs[0].initial_env assert 'OH_ENABLE_VNC' in specs[0].initial_env assert 'LOG_JSON' in specs[0].initial_env - assert specs[0].working_dir == '/home/openhands/workspace' + assert specs[0].working_dir == '/workspace/project' @patch( 'openhands.app_server.sandbox.docker_sandbox_spec_service._global_docker_client',