From 2bd9ad37c72174cbaa80e6adca4755fe51caa46b Mon Sep 17 00:00:00 2001 From: Victor Skvortsov Date: Tue, 9 Jun 2026 15:27:19 +0500 Subject: [PATCH] Make zed:// links clickable in terminal --- mkdocs/docs/concepts/dev-environments.md | 2 ++ .../_internal/core/models/configurations.py | 4 ++-- .../server/services/ides/__init__.py | 4 +++- .../_internal/server/services/ides/zed.py | 19 +++++++++++++++++++ .../core/models/test_configurations.py | 10 ++++++++++ 5 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 src/dstack/_internal/server/services/ides/zed.py diff --git a/mkdocs/docs/concepts/dev-environments.md b/mkdocs/docs/concepts/dev-environments.md index c4bb015653..2e4bb73f11 100644 --- a/mkdocs/docs/concepts/dev-environments.md +++ b/mkdocs/docs/concepts/dev-environments.md @@ -65,6 +65,8 @@ To connect via SSH, use: `ssh vscode` `dstack apply` automatically provisions an instance and sets up an IDE on it. +The `ide` property supports `vscode`, `cursor`, `windsurf`, and `zed`. + ??? info "SSH-only" The `ide` property is optional. If omitted, no IDE is pre-installed, but the dev environment is still accessible via SSH: diff --git a/src/dstack/_internal/core/models/configurations.py b/src/dstack/_internal/core/models/configurations.py index bacf34e5ab..dd7881791d 100644 --- a/src/dstack/_internal/core/models/configurations.py +++ b/src/dstack/_internal/core/models/configurations.py @@ -685,9 +685,9 @@ def check_image_or_commands_present(cls, values): class DevEnvironmentConfigurationParams(CoreModel): ide: Annotated[ - Optional[Union[Literal["vscode"], Literal["cursor"], Literal["windsurf"]]], + Optional[Union[Literal["vscode"], Literal["cursor"], Literal["windsurf"], Literal["zed"]]], Field( - description="The IDE to pre-install. Supported values include `vscode`, `cursor`, and `windsurf`. Defaults to no IDE (SSH only)" + description="The IDE to pre-install. Supported values include `vscode`, `cursor`, `windsurf`, and `zed`. Defaults to no IDE (SSH only)" ), ] = None version: Annotated[ diff --git a/src/dstack/_internal/server/services/ides/__init__.py b/src/dstack/_internal/server/services/ides/__init__.py index 377fc139c9..f22987aafb 100644 --- a/src/dstack/_internal/server/services/ides/__init__.py +++ b/src/dstack/_internal/server/services/ides/__init__.py @@ -4,13 +4,15 @@ from dstack._internal.server.services.ides.cursor import CursorDesktop from dstack._internal.server.services.ides.vscode import VSCodeDesktop from dstack._internal.server.services.ides.windsurf import WindsurfDesktop +from dstack._internal.server.services.ides.zed import ZedDesktop -_IDELiteral = Literal["vscode", "cursor", "windsurf"] +_IDELiteral = Literal["vscode", "cursor", "windsurf", "zed"] _ide_literal_to_ide_class_map: dict[_IDELiteral, type[IDE]] = { "vscode": VSCodeDesktop, "cursor": CursorDesktop, "windsurf": WindsurfDesktop, + "zed": ZedDesktop, } diff --git a/src/dstack/_internal/server/services/ides/zed.py b/src/dstack/_internal/server/services/ides/zed.py new file mode 100644 index 0000000000..851a6f83de --- /dev/null +++ b/src/dstack/_internal/server/services/ides/zed.py @@ -0,0 +1,19 @@ +from typing import Optional + +from dstack._internal.server.services.ides.base import IDE + + +class ZedDesktop(IDE): + name = "Zed" + url_scheme = "zed" + + def get_install_commands( + self, version: Optional[str] = None, extensions: Optional[list[str]] = None + ) -> list[str]: + # We don't need to pre-install any extensions for Zed so we let it + # auto-install the remote server into ~/.zed_server on the first SSH connect, + # downloading the binary that matches the connecting Zed client version. + return [] + + def get_url(self, authority: str, working_dir: str) -> str: + return f"zed://ssh/{authority}{working_dir}" diff --git a/src/tests/_internal/core/models/test_configurations.py b/src/tests/_internal/core/models/test_configurations.py index 5027e29733..0a928cb713 100644 --- a/src/tests/_internal/core/models/test_configurations.py +++ b/src/tests/_internal/core/models/test_configurations.py @@ -817,6 +817,16 @@ def test_cursor_version_not_validated(self): assert params.ide == "cursor" assert params.version == "0.40.0" + def test_zed_ide_allowed(self): + params = DevEnvironmentConfigurationParams(ide="zed") + assert params.ide == "zed" + assert params.version is None + + def test_zed_version_not_validated(self): + params = DevEnvironmentConfigurationParams(ide="zed", version="0.100.0") + assert params.ide == "zed" + assert params.version == "0.100.0" + def test_ide_optional(self): params = DevEnvironmentConfigurationParams() assert params.ide is None