From d88e68eb498bbd1d52f8855f15d33ececa2de390 Mon Sep 17 00:00:00 2001 From: greese-insight <124088146+greese-insight@users.noreply.github.com> Date: Thu, 31 Jul 2025 15:21:33 -0400 Subject: [PATCH] fix: update openhands local runtime to handle provider tokens correctly (#9915) Co-authored-by: Engel Nyst --- openhands/core/main.py | 5 ++++ openhands/core/setup.py | 55 ++++++++++++++++++++++++++++++----------- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/openhands/core/main.py b/openhands/core/main.py index 3979d6941d..1dd58fb117 100644 --- a/openhands/core/main.py +++ b/openhands/core/main.py @@ -24,6 +24,7 @@ from openhands.core.setup import ( create_memory, create_runtime, generate_sid, + get_provider_tokens, initialize_repository_for_runtime, ) from openhands.events import EventSource, EventStreamSubscriber @@ -103,11 +104,14 @@ async def run_controller( # when the runtime is created, it will be connected and clone the selected repository repo_directory = None if runtime is None: + # In itialize repository if needed + repo_tokens = get_provider_tokens() runtime = create_runtime( config, sid=sid, headless_mode=headless_mode, agent=agent, + git_provider_tokens=repo_tokens, ) # Connect to the runtime call_async_from_sync(runtime.connect) @@ -116,6 +120,7 @@ async def run_controller( if config.sandbox.selected_repo: repo_directory = initialize_repository_for_runtime( runtime, + immutable_provider_tokens=repo_tokens, selected_repository=config.sandbox.selected_repo, ) diff --git a/openhands/core/setup.py b/openhands/core/setup.py index 3a210b768a..80efb9cef9 100644 --- a/openhands/core/setup.py +++ b/openhands/core/setup.py @@ -16,7 +16,11 @@ from openhands.core.config.config_utils import DEFAULT_WORKSPACE_MOUNT_PATH_IN_S from openhands.core.logger import openhands_logger as logger from openhands.events import EventStream from openhands.events.event import Event -from openhands.integrations.provider import ProviderToken, ProviderType +from openhands.integrations.provider import ( + PROVIDER_TOKEN_TYPE, + ProviderToken, + ProviderType, +) from openhands.llm.llm import LLM from openhands.memory.memory import Memory from openhands.microagent.microagent import BaseMicroagent @@ -33,6 +37,7 @@ def create_runtime( sid: str | None = None, headless_mode: bool = True, agent: Agent | None = None, + git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None, ) -> Runtime: """Create a runtime for the agent to run on. @@ -77,8 +82,10 @@ def create_runtime( sid=session_id, plugins=agent_cls.sandbox_plugins, headless_mode=headless_mode, + git_provider_tokens=git_provider_tokens, ) + # Log the plugins that have been registered with the runtime for debugging purposes logger.debug( f'Runtime created with plugins: {[plugin.name for plugin in runtime.plugins]}' ) @@ -86,19 +93,14 @@ def create_runtime( return runtime -def initialize_repository_for_runtime( - runtime: Runtime, selected_repository: str | None = None -) -> str | None: - """Initialize the repository for the runtime. - - Args: - runtime: The runtime to initialize the repository for. - selected_repository: (optional) The GitHub repository to use. +def get_provider_tokens(): + """ + Retrieve provider tokens from environment variables and return them as a dictionary. Returns: - The repository directory path if a repository was cloned, None otherwise. + A dictionary mapping ProviderType to ProviderToken if tokens are found, otherwise None. """ - # clone selected repository if provided + # Collect provider tokens from environment variables if available provider_tokens = {} if 'GITHUB_TOKEN' in os.environ: github_token = SecretStr(os.environ['GITHUB_TOKEN']) @@ -112,12 +114,37 @@ def initialize_repository_for_runtime( bitbucket_token = SecretStr(os.environ['BITBUCKET_TOKEN']) provider_tokens[ProviderType.BITBUCKET] = ProviderToken(token=bitbucket_token) + # Wrap provider tokens in UserSecrets if any tokens were found secret_store = ( UserSecrets(provider_tokens=provider_tokens) if provider_tokens else None # type: ignore[arg-type] ) - immutable_provider_tokens = secret_store.provider_tokens if secret_store else None + return secret_store.provider_tokens if secret_store else None + + +def initialize_repository_for_runtime( + runtime: Runtime, + immutable_provider_tokens: PROVIDER_TOKEN_TYPE | None = None, + selected_repository: str | None = None, +) -> str | None: + """ + Initialize the repository for the runtime by cloning or initializing it, + running setup scripts, and setting up git hooks if present. + + Args: + runtime: The runtime to initialize the repository for. + immutable_provider_tokens: (optional) Provider tokens to use for authentication. + selected_repository: (optional) The repository to use. + + Returns: + The repository directory path if a repository was cloned, None otherwise. + """ + # If provider tokens are not provided, attempt to retrieve them from the environment + if not immutable_provider_tokens: + immutable_provider_tokens = get_provider_tokens() logger.debug(f'Selected repository {selected_repository}.') + + # Clone or initialize the repository using the runtime repo_directory = call_async_from_sync( runtime.clone_or_init_repo, GENERAL_TIMEOUT, @@ -125,9 +152,9 @@ def initialize_repository_for_runtime( selected_repository, None, ) - # Run setup script if it exists + # Run setup script if it exists in the repository runtime.maybe_run_setup_script() - # Set up git hooks if pre-commit.sh exists + # Set up git hooks if pre-commit.sh exists in the repository runtime.maybe_setup_git_hooks() return repo_directory