Skip to content

Commit bc3fcc1

Browse files
committed
Merge remote-tracking branch 'upstream/main' into test-verify-agent-clean
2 parents 4598994 + 8f98cfd commit bc3fcc1

39 files changed

+4514
-1092
lines changed

.github/workflows/cli-build-binary-and-optionally-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
push:
88
branches: [main]
99
tags:
10-
- '*-cli'
10+
- '*'
1111
pull_request:
1212
branches: ['**']
1313

.github/workflows/pypi-release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ jobs:
2222
runs-on: blacksmith-2vcpu-ubuntu-2404
2323
permissions:
2424
id-token: write
25-
# Run when manually dispatched for "cli" OR for tag pushes that contain '-cli'
25+
# Run when manually dispatched for "cli" OR for any tag pushes
2626
if: |
2727
(github.event_name == 'workflow_dispatch' && github.event.inputs.reason == 'cli')
28-
|| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && contains(github.ref, '-cli'))
28+
|| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
2929
steps:
3030
- name: Checkout repository
3131
uses: actions/checkout@v4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,4 @@ $RECYCLE.BIN/
193193
*.log
194194
.coverage
195195
.pytest_cache/
196+
software-agent-sdk/

Makefile

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,36 @@
1-
.PHONY: help install install-dev test format clean run
1+
SHELL := /usr/bin/env bash
2+
.SHELLFLAGS := -eu -o pipefail -c
3+
4+
# Colors for output
5+
ECHO := printf '%b\n'
6+
GREEN := \033[32m
7+
YELLOW := \033[33m
8+
RED := \033[31m
9+
CYAN := \033[36m
10+
RESET := \033[0m
11+
12+
.PHONY: help install install-dev test format clean run check-uv-version build
13+
14+
check-uv-version:
15+
@$(ECHO) "$(YELLOW)Checking uv version...$(RESET)"
16+
@UV_VERSION=$$(uv --version | cut -d' ' -f2); \
17+
REQUIRED_VERSION=$(REQUIRED_UV_VERSION); \
18+
if [ "$$(printf '%s\n' "$$REQUIRED_VERSION" "$$UV_VERSION" | sort -V | head -n1)" != "$$REQUIRED_VERSION" ]; then \
19+
$(ECHO) "$(RED)Error: uv version $$UV_VERSION is less than required $$REQUIRED_VERSION$(RESET)"; \
20+
$(ECHO) "$(YELLOW)Please update uv with: uv self update$(RESET)"; \
21+
exit 1; \
22+
fi; \
23+
$(ECHO) "$(GREEN)uv version $$UV_VERSION meets requirements$(RESET)"
24+
25+
build: check-uv-version
26+
@$(ECHO) "$(CYAN)Setting up OpenHands V1 development environment...$(RESET)"
27+
@$(ECHO) "$(YELLOW)Installing dependencies with uv sync --dev...$(RESET)"
28+
@uv sync --dev
29+
@$(ECHO) "$(GREEN)Dependencies installed successfully.$(RESET)"
30+
@$(ECHO) "$(YELLOW)Setting up pre-commit hooks...$(RESET)"
31+
@uv run pre-commit install
32+
@$(ECHO) "$(GREEN)Pre-commit hooks installed successfully.$(RESET)"
33+
@$(ECHO) "$(GREEN)Build complete! Development environment is ready.$(RESET)"
234

335
# Default target
436
help:

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
# OpenHands V1 CLI
22

3-
A **lightweight, modern CLI** to interact with the OpenHands agent (powered by [OpenHands software-agent-sdk](https://github.com/OpenHands/software-agent-sdk)).
3+
A **lightweight, modern CLI** to interact with the OpenHands agent (powered by [OpenHands Software Agent SDK](https://github.com/OpenHands/software-agent-sdk)).
44

55
---
66

77
## Quickstart
88

99
- Prerequisites: Python 3.12+, curl
1010
- Install uv (package manager):
11+
1112
```bash
1213
curl -LsSf https://astral.sh/uv/install.sh | sh
1314
# Restart your shell so "uv" is on PATH, or follow the installer hint
1415
```
1516

1617
### Run the CLI locally
18+
1719
```bash
1820
make install
1921

@@ -24,6 +26,7 @@ uv run openhands
2426
```
2527

2628
### Build a standalone executable
29+
2730
```bash
2831
# Build (installs PyInstaller if needed)
2932
./build.sh --install-pyinstaller

build.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,70 @@ def test_version() -> bool:
293293
return False
294294

295295

296+
def test_acp_executable() -> bool:
297+
"""Test the ACP server in the built executable with JSON-RPC messages."""
298+
print("🧪 Testing ACP server in the built executable...")
299+
300+
# Import test utilities
301+
from openhands_cli.acp_impl.test_utils import test_jsonrpc_messages
302+
303+
exe_path = Path("dist/openhands")
304+
if not exe_path.exists():
305+
exe_path = Path("dist/openhands.exe")
306+
if not exe_path.exists():
307+
print("❌ Executable not found!")
308+
return False
309+
310+
if os.name != "nt":
311+
os.chmod(exe_path, 0o755)
312+
313+
# JSON-RPC messages to test
314+
test_messages = [
315+
{
316+
"jsonrpc": "2.0",
317+
"id": 1,
318+
"method": "initialize",
319+
"params": {
320+
"protocolVersion": 1,
321+
"clientCapabilities": {
322+
"fs": {"readTextFile": True, "writeTextFile": True},
323+
"terminal": True,
324+
"_meta": {"terminal_output": True, "terminal-auth": True},
325+
},
326+
"clientInfo": {"name": "zed", "title": "Zed", "version": "0.212.7"},
327+
},
328+
},
329+
{
330+
"jsonrpc": "2.0",
331+
"id": 2,
332+
"method": "session/new",
333+
"params": {
334+
"cwd": "/tmp",
335+
"mcpServers": [],
336+
},
337+
},
338+
]
339+
340+
# Run the test
341+
success, responses = test_jsonrpc_messages(
342+
str(exe_path),
343+
["acp"],
344+
test_messages,
345+
timeout_per_message=15.0, # Increased timeout for CI environments
346+
verbose=True,
347+
)
348+
349+
# Print summary
350+
print(f"\n{'=' * 60}")
351+
print("ACP Test Summary:")
352+
print(f" Messages sent: {len(test_messages)}")
353+
print(f" Responses received: {len(responses)}")
354+
print(f" Test result: {'✅ PASSED' if success else '❌ FAILED'}")
355+
print(f"{'=' * 60}")
356+
357+
return success
358+
359+
296360
# =================================================
297361
# SECTION: Main
298362
# =================================================
@@ -317,7 +381,7 @@ def main() -> int:
317381
)
318382

319383
parser.add_argument(
320-
"--no-build", action="store_true", help="Skip testing the built executable"
384+
"--no-build", action="store_true", help="Skip building the executable"
321385
)
322386

323387
args = parser.parse_args()
@@ -356,6 +420,11 @@ def main() -> int:
356420
print("❌ Executable test failed, build process failed")
357421
return 1
358422

423+
print("\n" + "=" * 60)
424+
if not test_acp_executable():
425+
print("❌ ACP test failed, build process failed")
426+
return 1
427+
359428
print("\n🎉 Build process completed!")
360429
print("📁 Check the 'dist/' directory for your executable")
361430

hooks/rthook_profile_imports.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
start_time = time.perf_counter()
2323

2424
if _bootstrap is not None:
25-
_orig_find_and_load = _bootstrap._find_and_loadn # type: ignore
25+
_orig_find_and_load = _bootstrap._find_and_load # type: ignore
2626

2727
def _timed_find_and_load(name, import_):
2828
preloaded = name in sys.modules # cache hit?

openhands-cli.spec

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ a = Analysis(
3636
*collect_data_files('openhands.sdk'),
3737
# Include package metadata for importlib.metadata
3838
*copy_metadata('fastmcp'),
39+
*copy_metadata('agent-client-protocol'),
3940
],
4041
hiddenimports=[
4142
# Explicitly include modules that might not be detected automatically
@@ -48,6 +49,8 @@ a = Analysis(
4849
*collect_submodules('tiktoken_ext'),
4950
*collect_submodules('litellm'),
5051
*collect_submodules('fastmcp'),
52+
# Include Agent Client Protocol (ACP) for 'openhands acp' command
53+
*collect_submodules('acp'),
5154
# Include mcp but exclude CLI parts that require typer
5255
'mcp.types',
5356
'mcp.client',

openhands_cli/acp_impl/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# ACP Implementation
2+
3+
## What is the Agent Client Protocol (ACP)?
4+
5+
The [Agent Client Protocol (ACP)](https://agentclientprotocol.com/protocol/overview) is a standardized communication protocol that enables code editors and IDEs to interact with AI agents. ACP defines how clients (like code editors) and agents (like OpenHands) communicate through a JSON-RPC 2.0 interface.
6+
7+
For more details about the protocol, see the [ACP documentation](https://agentclientprotocol.com/protocol/overview).
8+
9+
## Development Guide
10+
11+
### Setup with Zed IDE
12+
13+
Follow the documentation at [OpenHands ACP Guide](https://docs.openhands.dev/openhands/usage/run-openhands/acp#zed-ide) for general setup instructions.
14+
15+
#### Option 1: Test with PR Branch
16+
17+
Add this agent configuration to test with the PR branch:
18+
19+
```json
20+
"OpenHands-uvx": {
21+
"command": "uvx",
22+
"args": [
23+
"--from",
24+
"git+https://github.com/OpenHands/OpenHands-CLI.git@xw/acp-simplification",
25+
"openhands",
26+
"acp"
27+
],
28+
"env": {}
29+
}
30+
```
31+
32+
#### Option 2: Launch Local Instance
33+
34+
Use this configuration to run your local development version:
35+
36+
```json
37+
"OpenHands-local": {
38+
"command": "uv",
39+
"args": [
40+
"run",
41+
"--project",
42+
"/YOUR_LOCAL_PATH/OpenHands-CLI",
43+
"openhands",
44+
"acp"
45+
],
46+
"env": {}
47+
}
48+
```
49+
50+
### Debugging
51+
52+
In Zed IDE, open ACP logs before starting a conversation:
53+
- Press `Cmd+Shift+P`
54+
- Search for **"dev: open acp log"**
55+
- This visualizes all events between the ACP server and client
56+
57+
### Testing with JSON-RPC CLI
58+
59+
To reproduce errors or test manually, send JSON-RPC events directly using the test script:
60+
61+
```bash
62+
uv run python scripts/acp/jsonrpc_cli.py ./dist/openhands acp
63+
```
64+
65+
This interactive CLI allows you to:
66+
- Send JSON-RPC messages as single lines
67+
- View stdout/stderr responses
68+
- Exit with `:q`, `:quit`, or `:exit`

openhands_cli/acp_impl/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""OpenHands Agent Client Protocol (ACP) Implementation."""

0 commit comments

Comments
 (0)