Compare commits

..

1 Commits

Author SHA1 Message Date
mamoodi 2ead80595c Release 0.50.0 2025-07-23 09:18:19 -04:00
15 changed files with 61 additions and 118 deletions
+1 -1
View File
@@ -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.49-nikolaik`
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.50-nikolaik`
## Develop inside Docker container
+3 -3
View File
@@ -62,17 +62,17 @@ system requirements and more information.
```bash
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49
docker.all-hands.dev/all-hands-ai/openhands:0.50
```
> **Note**: If you used OpenHands before version 0.44, you may want to run `mv ~/.openhands-state ~/.openhands` to migrate your conversation history to the new location.
+3 -3
View File
@@ -51,17 +51,17 @@ OpenHands也可以使用Docker在本地系统上运行。
```bash
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49
docker.all-hands.dev/all-hands-ai/openhands:0.50
```
> **注意**: 如果您在0.44版本之前使用过OpenHands,您可能需要运行 `mv ~/.openhands-state ~/.openhands` 来将对话历史迁移到新位置。
+3 -3
View File
@@ -42,17 +42,17 @@ OpenHandsはDockerを利用してローカル環境でも実行できます。
> 公共ネットワークで実行していますか?[Hardened Docker Installation Guide](https://docs.all-hands.dev/usage/runtimes/docker#hardened-docker-installation)を参照して、ネットワークバインディングの制限や追加のセキュリティ対策を実施してください。
```bash
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49
docker.all-hands.dev/all-hands-ai/openhands:0.50
```
**注**: バージョン0.44以前のOpenHandsを使用していた場合は、会話履歴を移行するために `mv ~/.openhands-state ~/.openhands` を実行してください。
+1 -1
View File
@@ -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.49-nikolaik}
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.50-nikolaik}
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
ports:
+1 -1
View File
@@ -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.49-nikolaik}
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.50-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:
+2 -2
View File
@@ -103,7 +103,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.49-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik \
-e SANDBOX_USER_ID=$(id -u) \
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
-e LLM_API_KEY=$LLM_API_KEY \
@@ -112,7 +112,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.49 \
docker.all-hands.dev/all-hands-ai/openhands:0.50 \
python -m openhands.cli.main --override-cli-mode true
```
+2 -2
View File
@@ -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.49-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49 \
docker.all-hands.dev/all-hands-ai/openhands:0.50 \
python -m openhands.core.main -t "write a bash script that prints hi"
```
+4 -4
View File
@@ -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.49-nikolaik
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49
docker.all-hands.dev/all-hands-ai/openhands:0.50
```
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.49
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.50
Starting OpenHands...
Running OpenHands as root
14:22:13 - openhands:INFO: server_config.py:50 - Using config class None
+3 -3
View File
@@ -67,17 +67,17 @@ A system with a modern processor and a minimum of **4GB RAM** is recommended to
### Start the App
```bash
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49
docker.all-hands.dev/all-hands-ai/openhands:0.50
```
> **Note**: If you used OpenHands before version 0.44, you may want to run `mv ~/.openhands-state ~/.openhands` to migrate your conversation history to the new location.
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "openhands-frontend",
"version": "0.49.0",
"version": "0.50.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openhands-frontend",
"version": "0.49.0",
"version": "0.50.0",
"dependencies": {
"@heroui/react": "^2.8.1",
"@microlink/react-json-view": "^1.26.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "openhands-frontend",
"version": "0.49.0",
"version": "0.50.0",
"private": true,
"type": "module",
"engines": {
+1 -1
View File
@@ -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.49-nikolaik"
runtime_container_image = "docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik"
```
#### Additional Kubernetes Options
+33 -90
View File
@@ -4,7 +4,8 @@ from typing import Callable
@dataclass
class CommandResult:
"""Represents the result of a shell command execution.
"""
Represents the result of a shell command execution.
Attributes:
content (str): The output content of the command.
@@ -16,7 +17,9 @@ class CommandResult:
class GitHandler:
"""A handler for executing Git-related operations via shell commands."""
"""
A handler for executing Git-related operations via shell commands.
"""
def __init__(
self,
@@ -26,7 +29,8 @@ class GitHandler:
self.cwd: str | None = None
def set_cwd(self, cwd: str) -> None:
"""Sets the current working directory for Git operations.
"""
Sets the current working directory for Git operations.
Args:
cwd (str): The directory path.
@@ -34,7 +38,8 @@ class GitHandler:
self.cwd = cwd
def _is_git_repo(self) -> bool:
"""Checks if the current directory is a Git repository.
"""
Checks if the current directory is a Git repository.
Returns:
bool: True if inside a Git repository, otherwise False.
@@ -44,7 +49,8 @@ class GitHandler:
return output.content.strip() == 'true'
def _get_current_file_content(self, file_path: str) -> str:
"""Retrieves the current content of a given file.
"""
Retrieves the current content of a given file.
Args:
file_path (str): Path to the file.
@@ -56,7 +62,8 @@ class GitHandler:
return output.content
def _verify_ref_exists(self, ref: str) -> bool:
"""Verifies whether a specific Git reference exists.
"""
Verifies whether a specific Git reference exists.
Args:
ref (str): The Git reference to check.
@@ -68,71 +75,10 @@ class GitHandler:
output = self.execute(cmd, self.cwd)
return output.exit_code == 0
def _is_ahead_of_remote_branch(self, remote_branch: str) -> bool:
"""Checks if the current branch is ahead of the specified remote branch.
Args:
remote_branch (str): The remote branch reference (e.g., 'origin/feature-branch').
Returns:
bool: True if current branch is ahead, False otherwise.
"""
cmd = f'git --no-pager rev-list --count {remote_branch}..HEAD'
output = self.execute(cmd, self.cwd)
if output.exit_code != 0:
return False
return int(output.content.strip()) > 0
def _includes_merged_main_commits(self, remote_branch: str, default_branch: str) -> bool:
"""Checks if the local branch includes commits that were merged from the default branch.
Since the remote branch was last updated.
Args:
remote_branch (str): The remote branch reference (e.g., 'origin/feature-branch').
default_branch (str): The default branch name (e.g., 'main').
Returns:
bool: True if merged main commits are included in the diff.
"""
# Get commits that are in HEAD but not in remote_branch
cmd = f'git --no-pager log --oneline {remote_branch}..HEAD'
output = self.execute(cmd, self.cwd)
if output.exit_code != 0:
return False
local_commits = output.content.strip().splitlines()
if not local_commits:
return False
# Get commits that are in origin/default_branch but not in remote_branch
origin_default = f'origin/{default_branch}'
if not self._verify_ref_exists(origin_default):
return False
cmd = f'git --no-pager log --oneline {remote_branch}..{origin_default}'
output = self.execute(cmd, self.cwd)
if output.exit_code != 0:
return False
main_commits = output.content.strip().splitlines()
if not main_commits:
return False
# Extract commit hashes from both lists
local_hashes = {line.split()[0] for line in local_commits if line.strip()}
main_hashes = {line.split()[0] for line in main_commits if line.strip()}
# If there's significant overlap, we likely have merged main commits
overlap = local_hashes.intersection(main_hashes)
return len(overlap) >= min(2, len(main_hashes) // 2)
def _get_valid_ref(self) -> str | None:
"""Determines a valid Git reference for comparison using a hybrid approach.
- Uses origin/current_branch when it's the best representation of push status
- Falls back to merge-base when origin/current_branch includes merged main commits
"""
Determines a valid Git reference for comparison.
Returns:
str | None: A valid Git reference or None if no valid reference is found.
"""
@@ -144,19 +90,8 @@ class GitHandler:
ref_default_branch = 'origin/' + default_branch
ref_new_repo = '$(git --no-pager rev-parse --verify 4b825dc642cb6eb9a060e54bf8d69288fbee4904)' # compares with empty tree
# Hybrid logic: check if origin/current_branch exists and causes pollution
if self._verify_ref_exists(ref_current_branch):
# If we're ahead of remote and it includes merged main commits, use merge-base instead
if (self._is_ahead_of_remote_branch(ref_current_branch) and
self._includes_merged_main_commits(ref_current_branch, default_branch)):
# Try merge-base first to avoid pollution
if self._verify_ref_exists(ref_non_default_branch):
return ref_non_default_branch
# Otherwise use origin/current_branch for normal push workflow
return ref_current_branch
# Fallback to original logic
refs = [
ref_current_branch,
ref_non_default_branch,
ref_default_branch,
ref_new_repo,
@@ -168,7 +103,8 @@ class GitHandler:
return None
def _get_ref_content(self, file_path: str) -> str:
"""Retrieves the content of a file from a valid Git reference.
"""
Retrieves the content of a file from a valid Git reference.
Args:
file_path (str): The file path in the repository.
@@ -185,7 +121,8 @@ class GitHandler:
return output.content if output.exit_code == 0 else ''
def _get_default_branch(self) -> str:
"""Retrieves the primary Git branch name of the repository.
"""
Retrieves the primary Git branch name of the repository.
Returns:
str: The name of the primary branch.
@@ -195,7 +132,8 @@ class GitHandler:
return output.content.split()[-1].strip()
def _get_current_branch(self) -> str:
"""Retrieves the currently selected Git branch.
"""
Retrieves the currently selected Git branch.
Returns:
str: The name of the current branch.
@@ -205,7 +143,8 @@ class GitHandler:
return output.content.strip()
def _get_changed_files(self) -> list[str]:
"""Retrieves a list of changed files compared to a valid Git reference.
"""
Retrieves a list of changed files compared to a valid Git reference.
Returns:
list[str]: A list of changed file paths.
@@ -223,7 +162,8 @@ class GitHandler:
return output.content.splitlines()
def _get_untracked_files(self) -> list[dict[str, str]]:
"""Retrieves a list of untracked files in the repository. This is useful for detecting new files.
"""
Retrieves a list of untracked files in the repository. This is useful for detecting new files.
Returns:
list[dict[str, str]]: A list of dictionaries containing file paths and statuses.
@@ -238,7 +178,8 @@ class GitHandler:
)
def get_git_changes(self) -> list[dict[str, str]] | None:
"""Retrieves the list of changed files in the Git repository.
"""
Retrieves the list of changed files in the Git repository.
Returns:
list[dict[str, str]] | None: A list of dictionaries containing file paths and statuses. None if not a git repository.
@@ -254,7 +195,8 @@ class GitHandler:
return result
def get_git_diff(self, file_path: str) -> dict[str, str]:
"""Retrieves the original and modified content of a file in the repository.
"""
Retrieves the original and modified content of a file in the repository.
Args:
file_path (str): Path to the file.
@@ -272,7 +214,8 @@ class GitHandler:
def parse_git_changes(changes_list: list[str]) -> list[dict[str, str]]:
"""Parses the list of changed files and extracts their statuses and paths.
"""
Parses the list of changed files and extracts their statuses and paths.
Args:
changes_list (list[str]): List of changed file entries.
+1 -1
View File
@@ -6,7 +6,7 @@ requires = [
[tool.poetry]
name = "openhands-ai"
version = "0.49.1"
version = "0.50.0"
description = "OpenHands: Code Less, Make More"
authors = [ "OpenHands" ]
license = "MIT"