Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci-lint-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ jobs:

- name: Python lint & typecheck
run: |
uv run isort --check-only .
uv run black --check .
uv run ruff check .
uv run isort --check-only libs/python tests
uv run black --check libs/python tests
uv run ruff check libs/python tests
# Temporarily disabled due to untyped codebase
# uv run mypy .
2 changes: 1 addition & 1 deletion .github/workflows/ci-lint-typescript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ jobs:
run: node ./scripts/typescript-typecheck.js

- name: Prettier check
run: pnpm prettier --check "**/*.{ts,tsx,js,jsx,json,md,yaml,yml}"
run: pnpm prettier --check "libs/typescript/**/*.{ts,tsx,js,jsx,json,md,yaml,yml}"
5 changes: 5 additions & 0 deletions changelog/2026-05-18.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Highlights

- **Computer server bind default** — `computer_server` now binds to `127.0.0.1`
by default. Use `--host 0.0.0.0` when the server needs to accept external
connections.
14 changes: 9 additions & 5 deletions libs/python/agent/tests/test_predict_click_zero_coords.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
AnthropicHostedToolsConfig.predict_click directly, without requiring the full
cua_agent import chain (which needs cua-computer, cua-core, etc.).
"""

import pytest


Expand Down Expand Up @@ -41,6 +42,7 @@ def _make_items(x, y):

# --- Regression tests: these all FAIL with the old code, PASS with the fix ---


def test_zero_x_was_broken_before_fix():
"""Old code returns None for x=0; new code returns (0, y)."""
items = _make_items(0, 100)
Expand All @@ -64,6 +66,7 @@ def test_zero_zero_was_broken_before_fix():

# --- Positive tests: non-zero coordinates work in both old and new code ---


def test_nonzero_coordinates_still_work():
items = _make_items(512, 384)
assert _extract_click_coords_fixed(items) == (512, 384)
Expand All @@ -76,16 +79,17 @@ def test_returns_none_when_no_computer_call():

# --- Verify the actual fix is present in the source file ---


def test_source_uses_is_not_none_check():
"""Confirm the fix is applied in the real anthropic.py source."""
import pathlib

src = (
pathlib.Path(__file__).parent.parent
/ "cua_agent" / "loops" / "anthropic.py"
pathlib.Path(__file__).parent.parent / "cua_agent" / "loops" / "anthropic.py"
).read_text()
assert 'action.get("x") is not None and action.get("y") is not None' in src, (
"Fix not found in anthropic.py — the 'is not None' check is missing"
)
assert (
'action.get("x") is not None and action.get("y") is not None' in src
), "Fix not found in anthropic.py — the 'is not None' check is missing"
# Ensure the old buggy pattern is gone
assert (
'if action.get("x") and action.get("y"):' not in src
Expand Down
6 changes: 6 additions & 0 deletions libs/python/computer-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,16 @@ python -m computer_server
# Or with custom port
python -m computer_server --port 8080

# Allow external access explicitly
python -m computer_server --host 0.0.0.0

# With resolution scaling (useful for Retina displays or VMs)
python -m computer_server --width 1512 --height 982
```

By default the server binds to `127.0.0.1`. Deployments that need access from
other hosts should pass `--host 0.0.0.0` or another explicit interface.

This provides:

- HTTP API at `/ws`, `/cmd`, `/status` endpoints
Expand Down
4 changes: 3 additions & 1 deletion libs/python/computer-server/computer_server/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ def parse_args(args: Optional[List[str]] = None) -> argparse.Namespace:
help="Auto-detect and log the actual screen resolution at startup",
)
parser.add_argument(
"--host", default="0.0.0.0", help="Host to bind the server to (default: 0.0.0.0)"
"--host",
default="127.0.0.1",
help="Host to bind the server to (default: 127.0.0.1; use 0.0.0.0 for external access)",
)
parser.add_argument(
"--port", type=int, default=8000, help="Port to bind the server to (default: 8000)"
Expand Down
6 changes: 2 additions & 4 deletions libs/python/computer-server/computer_server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1171,10 +1171,8 @@ def normalize_key(key: str) -> str:
#
# See: https://github.com/trycua/cua/issues/1605
import unicodedata
if (
len(key) == 1
and unicodedata.category(key) not in ("Cc", "Cs", "Cn")
):

if len(key) == 1 and unicodedata.category(key) not in ("Cc", "Cs", "Cn"):
await self._auto.type_text(key)
else:
await self._auto.press_key(key)
Expand Down
5 changes: 3 additions & 2 deletions libs/python/computer-server/computer_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Server:

def __init__(
self,
host: str = "0.0.0.0",
host: str = "127.0.0.1",
port: int = 8000,
log_level: str = "info",
ssl_keyfile: Optional[str] = None,
Expand All @@ -45,7 +45,8 @@ def __init__(
Initialize the server.

Args:
host: Host to bind the server to
host: Host to bind the server to. Defaults to localhost; pass
"0.0.0.0" explicitly for external access.
port: Port to bind the server to
log_level: Logging level (debug, info, warning, error, critical)
ssl_keyfile: Path to SSL private key file (for HTTPS)
Expand Down
12 changes: 5 additions & 7 deletions libs/python/computer-server/tests/test_keypress_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@

import unicodedata
from unittest.mock import AsyncMock, MagicMock, patch

import pytest


def _is_printable_char(key: str) -> bool:
"""Mirror of the routing logic in DirectComputer.keypress()."""
return (
len(key) == 1
and unicodedata.category(key) not in ("Cc", "Cs", "Cn")
)
return len(key) == 1 and unicodedata.category(key) not in ("Cc", "Cs", "Cn")


class TestPrintableCharDetection:
Expand Down Expand Up @@ -50,7 +48,7 @@ def test_control_characters_are_not_printable(self):
# Cc category — should go through press_key, not type_text.
assert _is_printable_char("\x00") is False
assert _is_printable_char("\x1b") is False # ESC
assert _is_printable_char("\n") is False # newline
assert _is_printable_char("\n") is False # newline

def test_empty_string_is_not_printable(self):
assert _is_printable_char("") is False
Expand All @@ -62,7 +60,7 @@ async def test_keypress_single_printable_uses_type_text():
auto = MagicMock()
auto.type_text = AsyncMock()
auto.press_key = AsyncMock()
auto.hotkey = AsyncMock()
auto.hotkey = AsyncMock()

# Simulate the routing logic directly.
key = "a"
Expand Down Expand Up @@ -96,7 +94,7 @@ async def test_keypress_special_key_uses_press_key():
async def test_keypress_combo_uses_hotkey():
"""Multi-key combos must still call hotkey."""
auto = MagicMock()
auto.hotkey = AsyncMock()
auto.hotkey = AsyncMock()
auto.type_text = AsyncMock()
auto.press_key = AsyncMock()

Expand Down
2 changes: 1 addition & 1 deletion libs/python/cua-cli/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ mcp = [
]
# Skills recording with LLM captioning
skills = [
"litellm==1.80.0",
"litellm==1.86.2",
]
# Full installation
all = [
Expand Down
Loading
Loading