Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vscode url to runloop runtime #5300

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
50 changes: 48 additions & 2 deletions openhands/runtime/impl/runloop/runloop_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class RunloopRuntime(EventStreamRuntime):
"""The RunloopRuntime class is an EventStreamRuntime that utilizes Runloop Devbox as a runtime environment."""

_sandbox_port: int = 4444
_vscode_port: int = 4445

def __init__(
self,
Expand All @@ -109,6 +110,7 @@ def __init__(
env_vars: dict[str, str] | None = None,
status_callback: Callable | None = None,
attach_to_existing: bool = False,
headless_mode: bool = True,
):
assert config.runloop_api_key is not None, 'Runloop API key is required'
self.devbox: DevboxView | None = None
Expand All @@ -127,9 +129,11 @@ def __init__(
env_vars,
status_callback,
attach_to_existing,
headless_mode,
)
# Buffer for container logs
self.log_buffer: LogBuffer | None = None
self._vscode_url: str | None = None

@tenacity.retry(
stop=tenacity.stop_after_attempt(120),
Expand Down Expand Up @@ -192,7 +196,7 @@ def _create_new_devbox(self) -> DevboxView:
environment_variables={'DEBUG': 'true'} if self.config.debug else {},
prebuilt='openhands',
launch_parameters=LaunchParameters(
available_ports=[self._sandbox_port],
available_ports=[self._sandbox_port, self._vscode_port],
resource_size_request='LARGE',
),
metadata={'container-name': self.container_name},
Expand Down Expand Up @@ -221,7 +225,7 @@ async def connect(self):

# Hook up logs
self.log_buffer = RunloopLogBuffer(self.runloop_api_client, self.devbox.id)
self.api_url = f'https://{tunnel.url}'
self.api_url = tunnel.url
logger.info(f'Container started. Server url: {self.api_url}')

# End Runloop connect
Expand Down Expand Up @@ -273,3 +277,45 @@ def close(self, rm_all_containers: bool | None = True):

if self.devbox:
self.runloop_api_client.devboxes.shutdown(self.devbox.id)

@property
def vscode_url(self) -> str | None:
if self.vscode_enabled and self.devbox and self.devbox.status == 'running':
if self._vscode_url is not None:
return self._vscode_url

try:
with send_request(
self.session,
'GET',
f'{self.api_url}/vscode/connection_token',
timeout=10,
) as response:
response_json = response.json()
assert isinstance(response_json, dict)
if response_json['token'] is None:
return None
token = response_json['token']

self._vscode_url = (
self.runloop_api_client.devboxes.create_tunnel(
id=self.devbox.id,
port=self._vscode_port,
).url
+ f'/?tkn={token}&folder={self.config.workspace_mount_path_in_sandbox}'
)

self.log(
'debug',
f'VSCode URL: {self._vscode_url}',
)

return self._vscode_url
except Exception as e:
self.log(
'error',
f'Failed to create vscode tunnel {e}',
)
return None
else:
return None
Loading