Compare commits

...

5 Commits

Author SHA1 Message Date
openhands
14bec04b4b Simplify setup script handling and remove session.py changes 2025-01-02 21:48:16 +00:00
openhands
3bdaf0abaa Use runtime file operations for setup script 2025-01-02 21:43:23 +00:00
openhands
d0ba5882a0 Simplify setup script handling 2025-01-02 21:42:36 +00:00
openhands
a1b40f1550 Add support for .openhands/setup.sh script
- Add maybe_run_setup_script method to Runtime class
- Run setup script after cloning repository
- Support both workspace and repository setup scripts
2025-01-02 21:13:21 +00:00
openhands
8f14520a87 Add support for .openhands/setup.sh script
- Add _run_setup_script method to Session class to run setup.sh after runtime initialization
- Add get_full_path method to FileStore interface and implementations
- Run setup script with proper permissions and error handling
2025-01-02 20:59:42 +00:00
9 changed files with 57 additions and 1 deletions

View File

@@ -219,6 +219,24 @@ class Runtime(FileEditRuntimeMixin):
self.log('info', f'Cloning repo: {selected_repository}')
self.run_action(action)
def maybe_run_setup_script(self, selected_repository: str | None):
"""Run .openhands/setup.sh if it exists in the workspace or repository."""
setup_script = '.openhands/setup.sh'
if selected_repository:
repo_name = selected_repository.split('/')[1]
setup_script = f'{repo_name}/.openhands/setup.sh'
# Try to read the setup script
read_obs = self.read(FileReadAction(path=setup_script))
if isinstance(read_obs, ErrorObservation):
return
# Execute the script
action = CmdRunAction(f'chmod +x {setup_script} && {setup_script}')
obs = self.run_action(action)
if isinstance(obs, CmdOutputObservation) and obs.exit_code != 0:
self.log('error', f'Setup script failed: {obs.content}')
def get_custom_microagents(self, selected_repository: str | None) -> list[str]:
custom_microagents_content = []
custom_microagents_dir = Path('.openhands') / 'microagents'

View File

@@ -5,6 +5,11 @@ class E2BFileStore(FileStore):
def __init__(self, filesystem):
self.filesystem = filesystem
def get_full_path(self, path: str) -> str:
if path.startswith('/'):
path = path[1:]
return path
def write(self, path: str, contents: str) -> None:
self.filesystem.write(path, contents)

View File

@@ -202,6 +202,7 @@ class AgentSession:
return
self.runtime.clone_repo(github_token, selected_repository)
self.runtime.maybe_run_setup_script(selected_repository)
if agent.prompt_manager:
microagents = await call_sync_from_async(
self.runtime.get_custom_microagents, selected_repository

View File

@@ -112,6 +112,9 @@ class Session:
github_token=github_token,
selected_repository=selected_repository,
)
# Run setup script if it exists
await self._run_setup_script()
except Exception as e:
logger.exception(f'Error creating controller: {e}')
await self.send_error(
@@ -206,4 +209,4 @@ class Session:
"""Queues a status message to be sent asynchronously."""
asyncio.run_coroutine_threadsafe(
self._send_status_message(msg_type, id, message), self.loop
)
)

View File

@@ -2,6 +2,18 @@ from abc import abstractmethod
class FileStore:
@abstractmethod
def get_full_path(self, path: str) -> str:
"""Get the full path for a given relative path.
Args:
path: The relative path.
Returns:
The full path.
"""
pass
@abstractmethod
def write(self, path: str, contents: str) -> None:
pass

View File

@@ -19,6 +19,11 @@ class GoogleCloudFileStore(FileStore):
self.storage_client = storage.Client()
self.bucket = self.storage_client.bucket(bucket_name)
def get_full_path(self, path: str) -> str:
if path.startswith('/'):
path = path[1:]
return path
def write(self, path: str, contents: str | bytes) -> None:
blob = self.bucket.blob(path)
with blob.open('w') as f:

View File

@@ -12,6 +12,11 @@ class InMemoryFileStore(FileStore):
def __init__(self, files: dict[str, str] = IN_MEMORY_FILES):
self.files = files
def get_full_path(self, path: str) -> str:
if path.startswith('/'):
path = path[1:]
return path
def write(self, path: str, contents: str) -> None:
self.files[path] = contents

View File

@@ -15,6 +15,11 @@ class S3FileStore(FileStore):
self.bucket = os.getenv('AWS_S3_BUCKET')
self.client = Minio(endpoint, access_key, secret_key, secure=secure)
def get_full_path(self, path: str) -> str:
if path.startswith('/'):
path = path[1:]
return path
def write(self, path: str, contents: str) -> None:
as_bytes = contents.encode('utf-8')
stream = io.BytesIO(as_bytes)

View File

@@ -100,6 +100,7 @@ reportlab = "*"
[tool.coverage.run]
concurrency = ["gevent"]
[tool.poetry.group.runtime.dependencies]
jupyterlab = "*"
notebook = "*"
@@ -129,6 +130,7 @@ ignore = ["D1"]
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.poetry.group.evaluation.dependencies]
streamlit = "*"
whatthepatch = "*"