feat: global port conflict detection for concurrent headroom wrap sessions#1121
feat: global port conflict detection for concurrent headroom wrap sessions#1121hareeshkar wants to merge 3 commits into
Conversation
Add _find_available_port(start_port, max_attempts) that skips ports occupied by another headroom proxy. Integrated into all three code paths: Path A — _launch_tool (aider, copilot, codex, goose, openhands) Path B — _run_proxy_only_watcher (cursor, cline, continue) Path C — claude() (Claude Code — custom proxy lifecycle) When port 8787 is occupied by claude, codex/aider/copilot auto-start on 8788. Works for all agents, all combinations, all orderings. Verification: - 9 unit tests: _find_available_port behavior (free, occupied, gap, range exhaustion, port range message) - 4 integration tests: source inspection verifies _find_available_port appears before _ensure_proxy in all 3 code paths, guarded by 'not no_proxy' condition - 110 CI precheck tests unaffected
PR governanceThis PR follows the template and is marked ready for human review. |
JerrettDavis
left a comment
There was a problem hiding this comment.
The port-conflict change needs cleanup before merge. The PR adds local Serena project files (.serena/.gitignore, .serena/project.yml) and unrelated uv.lock churn for package version/litellm markers. Those are outside the feature behavior and should be removed from this PR so the review and merge only cover the port-selection change. After that, the remaining wrap.py/test changes can be reviewed on their own merits.
De-duplicate: _find_available_port and its integration into _launch_tool, _run_proxy_only_watcher, and claude() belong only in PR headroomlabs-ai#1121 (global-port-conflict-detection). OpenCode handles port assignment through its own bounded loop with no_proxy=True to _launch_tool. Removed: - _find_available_port() function definition - 3 integration points (_launch_tool, _run_proxy_only_watcher, claude()) - tests/test_cli/test_port_conflict.py (13 tests → lives in headroomlabs-ai#1121) Retained: - OpenCode bounded port loop with max 10 attempts - All 57 original opencode tests
JerrettDavis
left a comment
There was a problem hiding this comment.
The port selection implementation is close, but this PR still has unrelated uv.lock churn in the current diff. The lockfile changes bump the local package version and alter LiteLLM dependency markers; that is not part of global port conflict detection and should not ride along with this feature.
Please remove the uv.lock changes from this PR so the diff is limited to headroom/cli/wrap.py and the focused port-conflict tests. Once the scope is clean, the wrap-path behavior and CI can be reviewed/merged without accidentally changing dependency metadata.
Description
When one headroom proxy is already running (e.g.
headroom wrap claudeonport 8787), starting another agent (
headroom wrap codex,headroom wrap aider) fails because port 8787 is occupied. This PR adds global portconflict detection so any agent started second automatically uses the
next available port — no manual
--portflags needed.Integrated into all three proxy startup paths used by every agent.
Type of Change
Changes Made
_find_available_port(start_port, max_attempts=10)helper in wrap.py_launch_tool,_run_proxy_only_watcher, andclaude()not no_proxyso--no-proxybypasses detectionTesting
Test Output
Real Behavior Proof
headroom wrap claudein terminal 1 (port 8787), thenheadroom wrap codexin terminal 2Review Readiness
Additional Notes