mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
17 Commits
test-ruff
...
enable-jir
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8f2bb2a7d | ||
|
|
d20d39c83a | ||
|
|
b21701e1ad | ||
|
|
116f0c0514 | ||
|
|
0e7230451e | ||
|
|
2c114ccea4 | ||
|
|
9363aec4e0 | ||
|
|
7b7761c588 | ||
|
|
71fbe17ec1 | ||
|
|
37daf068c5 | ||
|
|
5452abe513 | ||
|
|
4bd14c9dfc | ||
|
|
641a9e78bd | ||
|
|
8e9e5dd11a | ||
|
|
9323267898 | ||
|
|
23a98b452b | ||
|
|
7f530c33ae |
@@ -159,7 +159,7 @@ poetry run pytest ./tests/unit/test_*.py
|
||||
To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker
|
||||
container image by setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image.
|
||||
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.57-nikolaik`
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.58-nikolaik`
|
||||
|
||||
## Develop inside Docker container
|
||||
|
||||
|
||||
@@ -76,17 +76,17 @@ You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)
|
||||
You can also run OpenHands directly with Docker:
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.57-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.58-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.57-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.58-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.57
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.58
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -12,7 +12,7 @@ services:
|
||||
- SANDBOX_API_HOSTNAME=host.docker.internal
|
||||
- DOCKER_HOST_ADDR=host.docker.internal
|
||||
#
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.57-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.58-nikolaik}
|
||||
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -27,7 +27,7 @@ repos:
|
||||
- id: ruff
|
||||
entry: ruff check --config dev_config/python/ruff.toml
|
||||
types_or: [python, pyi, jupyter]
|
||||
args: [--fix, --unsafe-fixes, --verbose]
|
||||
args: [--fix, --unsafe-fixes]
|
||||
exclude: ^(third_party/|enterprise/)
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
|
||||
@@ -7,7 +7,7 @@ services:
|
||||
image: openhands:latest
|
||||
container_name: openhands-app-${DATE:-}
|
||||
environment:
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.57-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.58-nikolaik}
|
||||
#- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of ~/.openhands for this user
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
---
|
||||
title: Jira Cloud Integration (Coming soon...)
|
||||
title: Jira Cloud Integration
|
||||
description: Complete guide for setting up Jira Cloud integration with OpenHands Cloud, including service account creation, API token generation, webhook configuration, and workspace integration setup.
|
||||
---
|
||||
|
||||
# Jira Cloud Integration
|
||||
|
||||
<Note>
|
||||
The workspace name is the host part of your Jira Cloud URL.
|
||||
|
||||
For example, if your Jira URL is `https://all-hands.atlassian.net/browse/OH-55`,
|
||||
then your **workspace name** is **all-hands.atlassian.net**.
|
||||
|
||||
You will need this when connecting to Jira from OpenHands Cloud.
|
||||
</Note>
|
||||
|
||||
<Accordion title="🛠️ Setup Instructions (Admin Only)">
|
||||
|
||||
## Platform Configuration
|
||||
|
||||
### Step 1: Create Service Account
|
||||
@@ -26,7 +37,8 @@ description: Complete guide for setting up Jira Cloud integration with OpenHands
|
||||
|
||||
1. **Access Service Account Configuration**
|
||||
- Locate the created service account from above step and click on it
|
||||
- Click **Create API token**
|
||||
- Click **Create Credentials**
|
||||
- Select **API Token**
|
||||
- Set the expiry to 365 days (maximum allowed value)
|
||||
- Click **Next**
|
||||
- In **Select token scopes** screen, filter by following values
|
||||
@@ -75,21 +87,13 @@ description: Complete guide for setting up Jira Cloud integration with OpenHands
|
||||
2. **Configure Workspace**
|
||||
- Click **Configure** button
|
||||
- Enter your workspace name and click **Connect**
|
||||
- **Important:** Make sure you enter the full workspace name, eg: **yourcompany.atlassian.net**
|
||||
- **Important:** Make sure you enter the full workspace name, e.g. **yourcompany.atlassian.net**
|
||||
- If no integration exists, you'll be prompted to enter additional credentials required for the workspace integration:
|
||||
- **Webhook Secret**: The webhook secret from Step 3 above
|
||||
- **Service Account Email**: The service account email from Step 1 above
|
||||
- **Service Account API Key**: The API token from Step 2 above
|
||||
- Ensure **Active** toggle is enabled
|
||||
|
||||
<Note>
|
||||
Workspace name is the host name when accessing a resource in Jira Cloud.
|
||||
|
||||
Eg: https://all-hands.atlassian.net/browse/OH-55
|
||||
|
||||
Here the workspace name is **all-hands**.
|
||||
</Note>
|
||||
|
||||
3. **Complete OAuth Flow**
|
||||
- You'll be redirected to Jira Cloud to complete OAuth verification
|
||||
- Grant the necessary permissions to verify your workspace access.
|
||||
@@ -109,6 +113,24 @@ Here the workspace name is **all-hands**.
|
||||
- This will deactivate your workspace link
|
||||
- **Important:** If the original user who configured the integration chooses to unlink their integration, any users currently linked to that workspace integration will also be unlinked, and the workspace integration will be deactivated. The integration can only be reactivated by the original user.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="👥 Setup Instructions (Regular Users)">
|
||||
|
||||
If your **admin has already completed the Jira Cloud integration** for your company, you only need to:
|
||||
|
||||
1. **Log in to OpenHands Cloud**
|
||||
- Go to [OpenHands Cloud](https://app.all-hands.dev/)
|
||||
- Sign in with your Git provider (GitHub, GitLab, or BitBucket)
|
||||
|
||||
2. **Connect to Your Company Jira Workspace**
|
||||
- Go to **Settings** > **Integrations**
|
||||
- Locate the **Jira Cloud** section
|
||||
- Click **Configure**
|
||||
- Enter your company’s Jira workspace URL (e.g., `yourcompany.atlassian.net`)
|
||||
- Complete the OAuth flow when prompted
|
||||
</Accordion>
|
||||
|
||||
### Screenshots
|
||||
|
||||
<AccordionGroup>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Project Management Tool Integrations (Coming soon...)
|
||||
title: Project Management Tool Integrations
|
||||
description: Overview of OpenHands Cloud integrations with project management platforms including Jira Cloud, Jira Data Center, and Linear. Learn about setup requirements, usage methods, and troubleshooting.
|
||||
---
|
||||
|
||||
@@ -18,7 +18,7 @@ Integration requires two levels of setup:
|
||||
2. **Workspace Integration** - Self-service configuration through the OpenHands Cloud UI to link your OpenHands account to the target workspace
|
||||
|
||||
### Platform-Specific Setup Guides:
|
||||
- [Jira Cloud Integration (Coming soon...)](./jira-integration.md)
|
||||
- [Jira Cloud Integration](./jira-integration.md)
|
||||
- [Jira Data Center Integration (Coming soon...)](./jira-dc-integration.md)
|
||||
- [Linear Integration (Coming soon...)](./linear-integration.md)
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ The conversation history will be saved in `~/.openhands/sessions`.
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.57-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.58-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -122,7 +122,7 @@ docker run -it \
|
||||
-v ~/.openhands:/.openhands \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.57 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.58 \
|
||||
python -m openhands.cli.entry --override-cli-mode true
|
||||
```
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ export GITHUB_TOKEN="your-token" # Required for repository operations
|
||||
# Run OpenHands
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.57-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.58-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -73,7 +73,7 @@ docker run -it \
|
||||
-v ~/.openhands:/.openhands \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.57 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.58 \
|
||||
python -m openhands.core.main -t "write a bash script that prints hi"
|
||||
```
|
||||
|
||||
|
||||
@@ -68,23 +68,23 @@ Download and install the LM Studio desktop app from [lmstudio.ai](https://lmstud
|
||||
1. Check [the installation guide](/usage/local-setup) and ensure all prerequisites are met before running OpenHands, then run:
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.57-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.58-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.57-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.58-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.57
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.58
|
||||
```
|
||||
|
||||
2. Wait until the server is running (see log below):
|
||||
```
|
||||
Digest: sha256:e72f9baecb458aedb9afc2cd5bc935118d1868719e55d50da73190d3a85c674f
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.57
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.58
|
||||
Starting OpenHands...
|
||||
Running OpenHands as root
|
||||
14:22:13 - openhands:INFO: server_config.py:50 - Using config class None
|
||||
|
||||
@@ -116,17 +116,17 @@ Note that you'll still need `uv` installed for the default MCP servers to work p
|
||||
<Accordion title="Docker Command (Click to expand)">
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.57-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.58-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.57-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.58-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.57
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.58
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -27,7 +27,7 @@ repos:
|
||||
- id: ruff
|
||||
entry: ruff check --config enterprise/dev_config/python/ruff.toml
|
||||
types_or: [python, pyi, jupyter]
|
||||
args: [--fix, --verbose]
|
||||
args: [--fix]
|
||||
files: ^enterprise/
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
|
||||
@@ -5,6 +5,12 @@ from dataclasses import dataclass, field
|
||||
from uuid import uuid4
|
||||
|
||||
import socketio
|
||||
from server.logger import logger
|
||||
from server.utils.conversation_callback_utils import invoke_conversation_callbacks
|
||||
from storage.database import session_maker
|
||||
from storage.saas_settings_store import SaasSettingsStore
|
||||
from storage.stored_conversation_metadata import StoredConversationMetadata
|
||||
|
||||
from openhands.core.config import LLMConfig
|
||||
from openhands.core.config.openhands_config import OpenHandsConfig
|
||||
from openhands.core.config.utils import load_openhands_config
|
||||
@@ -15,6 +21,7 @@ from openhands.events.event_store_abc import EventStoreABC
|
||||
from openhands.events.observation import AgentStateChangedObservation
|
||||
from openhands.events.stream import EventStreamSubscriber
|
||||
from openhands.llm.llm_registry import LLMRegistry
|
||||
from openhands.runtime.runtime_status import RuntimeStatus
|
||||
from openhands.server.config.server_config import ServerConfig
|
||||
from openhands.server.conversation_manager.conversation_manager import (
|
||||
ConversationManager,
|
||||
@@ -31,12 +38,6 @@ from openhands.storage.files import FileStore
|
||||
from openhands.utils.async_utils import call_sync_from_async, wait_all
|
||||
from openhands.utils.shutdown_listener import should_continue
|
||||
|
||||
from server.logger import logger
|
||||
from server.utils.conversation_callback_utils import invoke_conversation_callbacks
|
||||
from storage.database import session_maker
|
||||
from storage.saas_settings_store import SaasSettingsStore
|
||||
from storage.stored_conversation_metadata import StoredConversationMetadata
|
||||
|
||||
# Time in seconds between cleanup operations for stale conversations
|
||||
_CLEANUP_INTERVAL_SECONDS = 15
|
||||
|
||||
@@ -686,6 +687,7 @@ class ClusteredConversationManager(StandaloneConversationManager):
|
||||
url=self._get_conversation_url(conversation_id),
|
||||
session_api_key=None,
|
||||
event_store=EventStore(conversation_id, self.file_store, uid),
|
||||
runtime_status=RuntimeStatus.READY,
|
||||
)
|
||||
)
|
||||
return results
|
||||
|
||||
@@ -60,9 +60,14 @@ from openhands.utils.utils import create_registry_and_conversation_stats
|
||||
RUNTIME_URL_PATTERN = os.getenv(
|
||||
'RUNTIME_URL_PATTERN', 'https://{runtime_id}.prod-runtime.all-hands.dev'
|
||||
)
|
||||
RUNTIME_ROUTING_MODE = os.getenv('RUNTIME_ROUTING_MODE', 'subdomain').lower()
|
||||
|
||||
# Pattern for base URL for the runtime
|
||||
RUNTIME_CONVERSATION_URL = RUNTIME_URL_PATTERN + '/api/conversations/{conversation_id}'
|
||||
RUNTIME_CONVERSATION_URL = RUNTIME_URL_PATTERN + (
|
||||
'/runtime/api/conversations/{conversation_id}'
|
||||
if RUNTIME_ROUTING_MODE == 'path'
|
||||
else '/api/conversations/{conversation_id}'
|
||||
)
|
||||
|
||||
# Time in seconds before a Redis entry is considered expired if not refreshed
|
||||
_REDIS_ENTRY_TIMEOUT_SECONDS = 300
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.57.0",
|
||||
"version": "0.58.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.57.0",
|
||||
"version": "0.58.0",
|
||||
"dependencies": {
|
||||
"@heroui/react": "^2.8.4",
|
||||
"@heroui/use-infinite-scroll": "^2.2.11",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.57.0",
|
||||
"version": "0.58.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
|
||||
@@ -129,11 +129,15 @@ class ActionExecutionClient(Runtime):
|
||||
return send_request(self.session, method, url, **kwargs)
|
||||
|
||||
def check_if_alive(self) -> None:
|
||||
request_url = f'{self.action_execution_server_url}/alive'
|
||||
self.log('debug', f'Sending request to: {request_url}')
|
||||
response = self._send_action_server_request(
|
||||
'GET',
|
||||
f'{self.action_execution_server_url}/alive',
|
||||
request_url,
|
||||
timeout=5,
|
||||
)
|
||||
self.log('debug', f'Response status code: {response.status_code}')
|
||||
self.log('debug', f'Response text: {response.text}')
|
||||
assert response.is_closed
|
||||
|
||||
def list_files(self, path: str | None = None) -> list[str]:
|
||||
|
||||
@@ -40,7 +40,7 @@ Two configuration options are required to use the Kubernetes runtime:
|
||||
2. **Runtime Container Image**: Specify the container image to use for the runtime environment
|
||||
```toml
|
||||
[sandbox]
|
||||
runtime_container_image = "docker.all-hands.dev/all-hands-ai/runtime:0.57-nikolaik"
|
||||
runtime_container_image = "docker.all-hands.dev/all-hands-ai/runtime:0.58-nikolaik"
|
||||
```
|
||||
|
||||
#### Additional Kubernetes Options
|
||||
|
||||
@@ -415,11 +415,19 @@ class RemoteRuntime(ActionExecutionClient):
|
||||
|
||||
def _wait_until_alive_impl(self) -> None:
|
||||
self.log('debug', f'Waiting for runtime to be alive at url: {self.runtime_url}')
|
||||
self.log(
|
||||
'debug',
|
||||
f'Sending request to: {self.config.sandbox.remote_runtime_api_url}/runtime/{self.runtime_id}',
|
||||
)
|
||||
runtime_info_response = self._send_runtime_api_request(
|
||||
'GET',
|
||||
f'{self.config.sandbox.remote_runtime_api_url}/runtime/{self.runtime_id}',
|
||||
)
|
||||
runtime_data = runtime_info_response.json()
|
||||
self.log(
|
||||
'debug',
|
||||
f'received response: {runtime_data}',
|
||||
)
|
||||
assert 'runtime_id' in runtime_data
|
||||
assert runtime_data['runtime_id'] == self.runtime_id
|
||||
assert 'pod_status' in runtime_data
|
||||
|
||||
@@ -13,6 +13,15 @@ from openhands.runtime.plugins.requirement import Plugin, PluginRequirement
|
||||
from openhands.runtime.utils import find_available_tcp_port
|
||||
from openhands.utils.shutdown_listener import should_continue
|
||||
|
||||
SU_TO_USER = os.getenv('SU_TO_USER', 'true').lower() in (
|
||||
'1',
|
||||
'true',
|
||||
't',
|
||||
'yes',
|
||||
'y',
|
||||
'on',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class JupyterRequirement(PluginRequirement):
|
||||
@@ -36,7 +45,7 @@ class JupyterPlugin(Plugin):
|
||||
|
||||
if not is_local_runtime:
|
||||
# Non-LocalRuntime
|
||||
prefix = f'su - {username} -s '
|
||||
prefix = f'su - {username} -s ' if SU_TO_USER else ''
|
||||
# cd to code repo, setup all env vars and run micromamba
|
||||
poetry_prefix = (
|
||||
'cd /openhands/code\n'
|
||||
|
||||
@@ -15,6 +15,16 @@ from openhands.runtime.plugins.requirement import Plugin, PluginRequirement
|
||||
from openhands.runtime.utils.system import check_port_available
|
||||
from openhands.utils.shutdown_listener import should_continue
|
||||
|
||||
RUNTIME_USERNAME = os.getenv('RUNTIME_USERNAME')
|
||||
SU_TO_USER = os.getenv('SU_TO_USER', 'true').lower() in (
|
||||
'1',
|
||||
'true',
|
||||
't',
|
||||
'yes',
|
||||
'y',
|
||||
'on',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class VSCodeRequirement(PluginRequirement):
|
||||
@@ -37,7 +47,7 @@ class VSCodePlugin(Plugin):
|
||||
)
|
||||
return
|
||||
|
||||
if username not in ['root', 'openhands']:
|
||||
if username not in filter(None, [RUNTIME_USERNAME, 'root', 'openhands']):
|
||||
self.vscode_port = None
|
||||
self.vscode_connection_token = None
|
||||
logger.warning(
|
||||
@@ -83,13 +93,19 @@ class VSCodePlugin(Plugin):
|
||||
if path_mode:
|
||||
base_path_flag = f' --server-base-path /{runtime_id}/vscode'
|
||||
|
||||
cmd = (
|
||||
f"su - {username} -s /bin/bash << 'EOF'\n"
|
||||
f'sudo chown -R {username}:{username} /openhands/.openvscode-server\n'
|
||||
f'cd {workspace_path}\n'
|
||||
f'exec /openhands/.openvscode-server/bin/openvscode-server --host 0.0.0.0 --connection-token {self.vscode_connection_token} --port {self.vscode_port} --disable-workspace-trust{base_path_flag}\n'
|
||||
'EOF'
|
||||
)
|
||||
cmd = (
|
||||
(
|
||||
f"su - {username} -s /bin/bash << 'EOF'\n"
|
||||
if SU_TO_USER
|
||||
else "/bin/bash << 'EOF'\n"
|
||||
)
|
||||
+ f'sudo chown -R {username}:{username} /openhands/.openvscode-server\n'
|
||||
+ f'cd {workspace_path}\n'
|
||||
+ 'exec /openhands/.openvscode-server/bin/openvscode-server '
|
||||
+ f'--host 0.0.0.0 --connection-token {self.vscode_connection_token} '
|
||||
+ f'--port {self.vscode_port} --disable-workspace-trust{base_path_flag}\n'
|
||||
+ 'EOF'
|
||||
)
|
||||
|
||||
# Using asyncio.create_subprocess_shell instead of subprocess.Popen
|
||||
# to avoid ASYNC101 linting error
|
||||
|
||||
@@ -20,6 +20,16 @@ from openhands.events.observation.commands import (
|
||||
from openhands.runtime.utils.bash_constants import TIMEOUT_MESSAGE_TEMPLATE
|
||||
from openhands.utils.shutdown_listener import should_continue
|
||||
|
||||
RUNTIME_USERNAME = os.getenv('RUNTIME_USERNAME')
|
||||
SU_TO_USER = os.getenv('SU_TO_USER', 'true').lower() in (
|
||||
'1',
|
||||
'true',
|
||||
't',
|
||||
'yes',
|
||||
'y',
|
||||
'on',
|
||||
)
|
||||
|
||||
|
||||
def split_bash_commands(commands: str) -> list[str]:
|
||||
if not commands.strip():
|
||||
@@ -193,7 +203,9 @@ class BashSession:
|
||||
def initialize(self) -> None:
|
||||
self.server = libtmux.Server()
|
||||
_shell_command = '/bin/bash'
|
||||
if self.username in ['root', 'openhands']:
|
||||
if SU_TO_USER and self.username in list(
|
||||
filter(None, [RUNTIME_USERNAME, 'root', 'openhands'])
|
||||
):
|
||||
# This starts a non-login (new) shell for the given user
|
||||
_shell_command = f'su {self.username} -'
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import os
|
||||
|
||||
from openhands.core.config import OpenHandsConfig
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.runtime.plugins import PluginRequirement
|
||||
@@ -12,6 +14,9 @@ DEFAULT_PYTHON_PREFIX = [
|
||||
]
|
||||
DEFAULT_MAIN_MODULE = 'openhands.runtime.action_execution_server'
|
||||
|
||||
RUNTIME_USERNAME = os.getenv('RUNTIME_USERNAME')
|
||||
RUNTIME_UID = os.getenv('RUNTIME_UID')
|
||||
|
||||
|
||||
def get_action_execution_server_startup_command(
|
||||
server_port: int,
|
||||
@@ -26,7 +31,10 @@ def get_action_execution_server_startup_command(
|
||||
sandbox_config = app_config.sandbox
|
||||
logger.debug(f'app_config {vars(app_config)}')
|
||||
logger.debug(f'sandbox_config {vars(sandbox_config)}')
|
||||
logger.debug(f'override_user_id {override_user_id}')
|
||||
logger.debug(f'RUNTIME_USERNAME {RUNTIME_USERNAME}, RUNTIME_UID {RUNTIME_UID}')
|
||||
logger.debug(
|
||||
f'override_username {override_username}, override_user_id {override_user_id}'
|
||||
)
|
||||
|
||||
# Plugin args
|
||||
plugin_args = []
|
||||
@@ -40,10 +48,15 @@ def get_action_execution_server_startup_command(
|
||||
'--browsergym-eval-env'
|
||||
] + sandbox_config.browsergym_eval_env.split(' ')
|
||||
|
||||
username = override_username or (
|
||||
'openhands' if app_config.run_as_openhands else 'root'
|
||||
username = (
|
||||
override_username
|
||||
or RUNTIME_USERNAME
|
||||
or ('openhands' if app_config.run_as_openhands else 'root')
|
||||
)
|
||||
user_id = override_user_id or (1000 if app_config.run_as_openhands else 0)
|
||||
user_id = (
|
||||
override_user_id or RUNTIME_UID or (1000 if app_config.run_as_openhands else 0)
|
||||
)
|
||||
logger.debug(f'username {username}, user_id {user_id}')
|
||||
|
||||
base_cmd = [
|
||||
*python_prefix,
|
||||
|
||||
@@ -27,6 +27,7 @@ class HttpSession:
|
||||
headers = kwargs.get('headers') or {}
|
||||
headers = {**self.headers, **headers}
|
||||
kwargs['headers'] = headers
|
||||
logger.debug(f'HttpSession:request called with args {args} and kwargs {kwargs}')
|
||||
return CLIENT.request(*args, **kwargs)
|
||||
|
||||
def stream(self, *args, **kwargs):
|
||||
|
||||
@@ -6,7 +6,7 @@ requires = [
|
||||
|
||||
[tool.poetry]
|
||||
name = "openhands-ai"
|
||||
version = "0.57.0"
|
||||
version = "0.58.0"
|
||||
description = "OpenHands: Code Less, Make More"
|
||||
authors = [ "OpenHands" ]
|
||||
license = "MIT"
|
||||
|
||||
Reference in New Issue
Block a user