Skip to content

Commit ad2237d

Browse files
zxh3Xiaohua Zhang
andauthored
feat: vscode support for modal runtime (#6442)
Co-authored-by: Xiaohua Zhang <xiaohua.dev@gmail.com>
1 parent aa0cd51 commit ad2237d

File tree

1 file changed

+31
-1
lines changed

1 file changed

+31
-1
lines changed

openhands/runtime/impl/modal/modal_runtime.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class ModalRuntime(ActionExecutionClient):
4040

4141
container_name_prefix = 'openhands-sandbox-'
4242
sandbox: modal.Sandbox | None
43+
sid: str
4344

4445
def __init__(
4546
self,
@@ -57,6 +58,7 @@ def __init__(
5758

5859
self.config = config
5960
self.sandbox = None
61+
self.sid = sid
6062

6163
self.modal_client = modal.Client.from_credentials(
6264
config.modal_api_token_id.get_secret_value(),
@@ -75,6 +77,8 @@ def __init__(
7577

7678
# This value is arbitrary as it's private to the container
7779
self.container_port = 3000
80+
self._vscode_port = 4445
81+
self._vscode_url: str | None = None
7882

7983
self.status_callback = status_callback
8084
self.base_container_image_id = self.config.sandbox.base_container_image
@@ -140,6 +144,7 @@ async def connect(self):
140144

141145
if not self.attach_to_existing:
142146
self.send_status_message(' ')
147+
self._runtime_initialized = True
143148

144149
def _get_action_execution_server_host(self):
145150
return self.api_url
@@ -208,6 +213,7 @@ def _init_sandbox(
208213
environment: dict[str, str | None] = {
209214
'port': str(self.container_port),
210215
'PYTHONUNBUFFERED': '1',
216+
'VSCODE_PORT': str(self._vscode_port),
211217
}
212218
if self.config.debug:
213219
environment['DEBUG'] = 'true'
@@ -225,7 +231,7 @@ def _init_sandbox(
225231
*sandbox_start_cmd,
226232
secrets=[env_secret],
227233
workdir='/openhands/code',
228-
encrypted_ports=[self.container_port],
234+
encrypted_ports=[self.container_port, self._vscode_port],
229235
image=self.image,
230236
app=self.app,
231237
client=self.modal_client,
@@ -248,3 +254,27 @@ def close(self):
248254

249255
if not self.attach_to_existing and self.sandbox:
250256
self.sandbox.terminate()
257+
258+
@property
259+
def vscode_url(self) -> str | None:
260+
if self._vscode_url is not None: # cached value
261+
self.log('debug', f'VSCode URL: {self._vscode_url}')
262+
return self._vscode_url
263+
token = super().get_vscode_token()
264+
if not token:
265+
self.log('error', 'VSCode token not found')
266+
return None
267+
if not self.sandbox:
268+
self.log('error', 'Sandbox not initialized')
269+
return None
270+
271+
tunnel = self.sandbox.tunnels()[self._vscode_port]
272+
tunnel_url = tunnel.url
273+
self._vscode_url = tunnel_url + f'/?tkn={token}&folder={self.config.workspace_mount_path_in_sandbox}'
274+
275+
self.log(
276+
'debug',
277+
f'VSCode URL: {self._vscode_url}',
278+
)
279+
280+
return self._vscode_url

0 commit comments

Comments
 (0)