Skip to content

Conversation

@malhotra5
Copy link
Collaborator

@malhotra5 malhotra5 commented Jan 17, 2026

Summary

This PR adds support for the openhands-workspace package and enables cloud mode in the TUI when running openhands cloud.

Changes

1. Added openhands-workspace dependency

  • Added openhands-workspace==1.8.2 to pyproject.toml alongside openhands-sdk and openhands-tools

2. Added cloud parameters to OpenHandsApp

  • Added cloud: bool = False and server_url: str | None = None parameters to OpenHandsApp.__init__
  • Added the same parameters to the main() function

3. Modified entrypoint for cloud command

  • Changed openhands cloud command to launch the TUI with cloud=True instead of the previous headless behavior
  • Passes server_url from args to the TUI

4. Create RemoteConversation in cloud mode

  • Added cloud and server_url parameters to setup_conversation()
  • Created new setup_cloud_conversation() function that:
    • Imports OpenHandsCloudWorkspace from openhands.workspace
    • Gets API key using require_api_key() from cloud conversation module
    • Creates OpenHandsCloudWorkspace instance
    • Creates RemoteConversation instead of local Conversation

Files Changed

  • pyproject.toml - Added openhands-workspace dependency
  • openhands_cli/entrypoint.py - Updated cloud command handling
  • openhands_cli/setup.py - Added cloud conversation setup
  • openhands_cli/tui/core/conversation_runner.py - Added cloud parameters
  • openhands_cli/tui/textual_app.py - Added cloud parameters to app and main function

@malhotra5 can click here to continue refining the PR


🚀 Try this PR

uvx --python 3.12 git+https://github.com/OpenHands/OpenHands-CLI.git@add-openhands-workspace-cloud-support

- Add openhands-workspace==1.8.2 dependency to pyproject.toml
- Add cloud and server_url parameters to OpenHandsApp
- Modify entrypoint to launch TUI with cloud=True for 'openhands cloud' command
- Create setup_cloud_conversation() that uses OpenHandsCloudWorkspace and RemoteConversation
- Update ConversationRunner to accept and pass cloud parameters

Co-authored-by: openhands <openhands@all-hands.dev>
Use cloud_api_url and cloud_api_key instead of server_url and conversation_id

Co-authored-by: openhands <openhands@all-hands.dev>
@malhotra5 malhotra5 changed the title Add openhands-workspace package and cloud mode support for TUI Feat: support real-time cloud conversation in local TUI Jan 17, 2026
openhands-agent and others added 14 commits January 17, 2026 00:32
- Merge main into branch
- Add ConversationStateUpdateEvent to imports in richlog_visualizer.py
- Add checks to ignore ConversationStateUpdateEvent in _create_event_widget and _create_event_collapsible
- Fix setup.py to use TokenStorage directly instead of removed require_api_key

Co-authored-by: openhands <openhands@all-hands.dev>
- Add on_conversation_ready callback to ConversationVisualizer
- Detect ConversationStateUpdateEvent in visualizer to signal cloud conversation is ready
- Show 'Setting up cloud conversation...' indicator when starting cloud mode
- Replace indicator with 'Cloud conversation ready!' message when ready
- Block user input until cloud conversation is ready with warning notification

Co-authored-by: openhands <openhands@all-hands.dev>
- Add test_cloud_setup_indicator: Shows 'Setting up cloud conversation...' message
- Add test_cloud_ready_indicator: Shows 'Cloud conversation ready!' message

Co-authored-by: openhands <openhands@all-hands.dev>
- Add get_conversation_info method to OpenHandsApiClient to fetch sandbox_id
- Add sandbox_id parameter throughout the cloud conversation setup chain
- Add _ensure_workspace_alive method to ConversationRunner to check/restart workspace
- Add resume argument (-r/--resume) to cloud command parser
- Add _fetch_cloud_sandbox_id helper to entrypoint for fetching sandbox info
- Pass sandbox_id to OpenHandsCloudWorkspace to reclaim existing sandbox

Co-authored-by: openhands <openhands@all-hands.dev>
- Move _fetch_cloud_sandbox_id from entrypoint.py to textual_app.py
- Rename to fetch_cloud_sandbox_id (public async function)
- Use ensure_valid_auth from auth.utils to handle authentication (dedup)
- Fetch sandbox_id inside main() instead of entrypoint
- Handle None return from main() in entrypoint

Co-authored-by: openhands <openhands@all-hands.dev>
- Create openhands_cli/cloud/utils.py with fetch_cloud_sandbox_id
- Create openhands_cli/cloud/__init__.py to export the function
- Simplify fetch_cloud_sandbox_id to return str | None (no exceptions)
- Move imports to top of textual_app.py
- Update main() to exit early when sandbox_id is None

Co-authored-by: openhands <openhands@all-hands.dev>
- Create CloudSetupIndicator widget with Braille spinner animation
- Update _show_cloud_setup_indicator to use the new widget
- Spinner animates at 100ms intervals showing process is running

Co-authored-by: openhands <openhands@all-hands.dev>
- Add CloudSetupIndicator import to test file
- Use CloudSetupIndicator widget in test instead of Static
- Yield indicator in compose instead of mounting in on_mount
- Fix CloudSetupIndicator to use markup=True and height:auto
- Initialize widget with visible text immediately

Co-authored-by: openhands <openhands@all-hands.dev>
RemoteConversation gets its HTTP client from workspace.client, which is
already configured with the correct host and api_key after the sandbox
starts. Passing host and api_key directly was causing them to be ignored.

Co-authored-by: openhands <openhands@all-hands.dev>
The cloud setup indicator was showing immediately on app launch when in
cloud mode. Now it only appears after the user submits their first message,
right before the conversation runner creates the cloud sandbox.

Co-authored-by: openhands <openhands@all-hands.dev>
The conversation runner creation was blocking the UI, preventing
notifications and the cloud setup indicator from appearing until after
the cloud sandbox was already set up.

Now the runner creation runs in a background worker using run_in_executor,
allowing the UI to remain responsive and show the setup indicator animation.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions
Copy link
Contributor

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands_cli
   entrypoint.py1273572%27, 116, 118–119, 122–126, 129–130, 132, 134, 142, 144–146, 148, 150–152, 155, 157, 173, 175, 179, 181, 203, 211, 213–215, 232, 249, 273
   setup.py661872%58, 65–68, 84, 187–189, 194–195, 198, 201, 209, 214, 222–224
openhands_cli/argparsers
   cloud_parser.py100100% 
openhands_cli/auth
   api_client.py1302283%75, 92–97, 100, 102–105, 133, 184, 212–213, 235, 240, 279, 344–346
openhands_cli/cloud
   __init__.py20100% 
   utils.py151220%19, 21–23, 25–32
openhands_cli/tui
   textual_app.py37512466%199–200, 203, 208, 236–237, 244, 251, 257, 261, 307, 313, 346, 371–372, 402, 419, 426, 429–430, 433–435, 438, 443–444, 446, 477–479, 482, 485–486, 488, 491–492, 495, 501, 513, 534, 536–537, 541–542, 549–551, 554, 565, 571–573, 579, 581, 586–587, 591, 599, 602, 604–605, 617–620, 622–628, 631–632, 640–641, 680, 682, 717–718, 720, 726, 744–745, 748, 751–756, 758, 760, 811–812, 859, 862, 864, 866, 868–870, 873, 875–877, 879–880, 884, 887, 889, 891, 893–894, 897, 900–902, 904, 951, 954–955, 960, 981
openhands_cli/tui/core
   conversation_runner.py16110236%100, 104, 108, 111–112, 114, 117, 123–124, 128–130, 137, 139, 150, 156, 166–167, 169, 171, 174–175, 182–183, 192, 197–199, 204, 206, 214–215, 217–218, 221–228, 230, 232, 234–235, 237, 241, 245–246, 249, 253–255, 257–258, 261, 265, 267, 271–273, 276, 284–285, 287, 291–292, 295–296, 299, 302, 304–305, 307–308, 310–311, 313, 316, 325, 328–329, 331, 336, 340–341, 346, 348, 354–355, 360, 362, 364, 371, 374, 379, 381, 388–389, 392–393
openhands_cli/tui/widgets
   __init__.py30100% 
   cloud_indicator.py240100% 
   richlog_visualizer.py2758967%64, 67, 71, 74–77, 79, 133, 157–160, 173, 189–191, 204, 207–208, 211–212, 215, 217, 220, 250–254, 258, 270, 305–307, 312–313, 316, 318–321, 323–326, 328–331, 333–335, 337, 345–347, 362–363, 365, 374–376, 397–398, 401, 443, 447, 450, 453, 472, 477, 479–481, 492, 496, 499, 502, 523–524, 528–530, 536–537, 541–543
TOTAL477477883% 

If None, defaults to AlwaysConfirm.
cloud: If True, use OpenHands Cloud for remote execution.
server_url: The OpenHands Cloud server URL (used when cloud=True).
sandbox_id: Optional sandbox ID to reclaim an existing sandbox.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick thought in passing: I wonder if we'll be happier if we make separate classes for remote conversation runner, and maybe for some other classes too. Some things are not the same, and my quick guess is that maybe it'll be easier to read/maintain/debug/not-make-bugs(tm) 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants