Skip to content

feat(ide): add vscode-web (code serve-web) browser IDE#532

Merged
skevetter merged 5 commits into
mainfrom
feat/ide-vscode-web
Jun 26, 2026
Merged

feat(ide): add vscode-web (code serve-web) browser IDE#532
skevetter merged 5 commits into
mainfrom
feat/ide-vscode-web

Conversation

@skevetter

@skevetter skevetter commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the official VS Code for the Web as --ide=vscode-web, running the official VS Code CLI as code serve-web.

IDE --ide= Marketplace
openvscode (existing) openvscode Open VSX (Gitpod fork)
code-server (existing) code-server Open VSX (Coder)
VS Code Web (new) vscode-web Microsoft Marketplace

The motivation is the Microsoft Marketplace: serve-web is the only browser option that serves Microsoft-only extensions (Pylance, MS C/C++), which the Open VSX-based IDEs can't.

Design

Mirrors the established one-package-per-browser-IDE convention (pkg/ide/codeserver was the template). Wired through every layer code-server uses:

  • pkg/config/ide.goIDEVSCodeWeb enum
  • pkg/ide/vscodeweb/ — new package (install/start/extensions), unit tests
  • cmd/internal/agentcontainer/setupVSCodeWeb + vscode-web-async command
  • pkg/ide/ideparse — registry entry
  • pkg/ide/opener — browser opener + tunnel label
  • pkg/ide/types.go — SSH auth-sock reuse
  • desktop (Svelte) — IDE pickers + distinct browser-window icon
  • e2e/tests/ide/ide.go — smoke case

Installs to ~/.vscode-web (distinct from the desktop vscode server's ~/.vscode-server), default port 10802, pinned VERSION (overridable via the IDE option). Started with --without-connection-token --accept-server-license-terms so the background process never blocks.

Notes for reviewers

  • Runtime network dependency: code serve-web fetches its server payload on the first browser connect (inside the detached helper), so the container needs outbound access to update.code.visualstudio.com. This requirement is logged before launch, but a network-blocked container will fail at connect rather than at setup — worth confirming the failure mode is acceptable.
  • Hosted icon asset: the web/pro UI loads IDE icons from devsy.sh/assets. The registry points at vscode-web.svg, which still needs to be uploaded there. The desktop bundle already ships its own copy.
  • Desktop type-check not run in this environment (node_modules absent). The Svelte edits are trivial literal list additions; please confirm npm run check is clean in CI.
  • Pinned version is just a default — bump as desired.
  • Trademark: the icon and vscode-web itself use Microsoft's official VS Code mark/builds, consistent with the existing vscode/vscodebrowser assets.

Testing

go build ./..., go test ./pkg/ide/... (101 pass), golangci-lint clean of new issues. A scoped dupl exclusion was added in .golangci.yaml for the intentionally-parallel per-IDE *_async.go / setup functions.

Summary by CodeRabbit

  • New Features

    • Added support for VS Code Web as a selectable IDE option across workspace setup and settings.
    • Improved browser-based IDE launching so VS Code Web can open with the right workspace connection behavior.
  • Bug Fixes

    • Added handling for VS Code Web during container setup and startup so it installs and runs correctly.
    • Updated several support/contact email references to the new address.
  • Tests

    • Expanded end-to-end coverage for starting workspaces with VS Code Web.

Adds the official "VS Code for the Web" as `--ide=vscode-web`, running the
official VS Code CLI as `code serve-web`. Unlike openvscode (Gitpod fork,
Open VSX) and code-server (Coder, Open VSX), this serves the full Microsoft
Marketplace, so Microsoft-only extensions (Pylance, MS C/C++) are available.

Wired through every layer that code-server uses, mirroring the
one-package-per-browser-IDE convention:
- config enum, ideparse registry, agent installIDE + async extension install
- browser opener + tunnel label, SSH auth-sock reuse
- desktop IDE pickers (Svelte) with a distinct browser-window icon

Installs to ~/.vscode-web (distinct from the desktop vscode server's
~/.vscode-server), default port 10802, pinned VERSION (overridable).
serve-web fetches its server payload on first connect, so the container
needs outbound network access; this requirement is logged before launch.
The web/pro UI loads IDE icons from devsy.sh/assets. Asset must be
uploaded there before the icon renders in that surface (desktop bundle
already ships its own copy).
@netlify

netlify Bot commented Jun 26, 2026

Copy link
Copy Markdown

Deploy Preview for images-devsy-sh canceled.

Name Link
🔨 Latest commit 06aacb0
🔍 Latest deploy log https://app.netlify.com/projects/images-devsy-sh/deploys/6a3e9dc6f298ad0008c272f2

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@skevetter, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 16 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 26d47e18-27ee-473b-818c-68df461a731e

📥 Commits

Reviewing files that changed from the base of the PR and between 4c423bb and 06aacb0.

📒 Files selected for processing (5)
  • .golangci.yaml
  • cmd/internal/agentcontainer/setup.go
  • pkg/ide/opener/opener.go
  • pkg/ide/vscodeweb/vscodeweb.go
  • pkg/ide/vscodeweb/vscodeweb_test.go
📝 Walkthrough

Walkthrough

Adds VS Code Web IDE support across config, parsing, backend setup, browser opening, desktop selection, and e2e coverage. Separately updates several email values to dev@devsy.sh.

Changes

VS Code Web support

Layer / File(s) Summary
IDE identifier and parser metadata
pkg/config/ide.go, pkg/ide/ideparse/parse.go, pkg/ide/opener/browser_tunnel_state.go, pkg/ide/types.go
Adds vscode-web to the IDE enum, parsing allow-list, tunnel label metadata, and auth-socket reuse logic.
VS Code Web package
pkg/ide/vscodeweb/*
Introduces the pkg/ide/vscodeweb package with install, start, extension, release URL, and path helpers.
VS Code Web tests
pkg/ide/vscodeweb/vscodeweb_test.go
Adds tests for release URL overrides and NewVSCodeWeb(ServerOptions{}) defaults.
Container setup and async install
.golangci.yaml, cmd/internal/agentcontainer/container.go, cmd/internal/agentcontainer/setup.go, cmd/internal/agentcontainer/vscodeweb_async.go
Registers vscode-web-async, routes IDEVSCodeWeb through container setup, starts extension installation in the background, and exempts the mirrored setup code from dupl.
Browser opener wiring
pkg/ide/opener/opener.go
Routes IDEVSCodeWeb to openVSCodeWebBrowser and uses VS Code Web bind, port, forwarding, label, and URL settings.
Desktop selection and e2e coverage
desktop/src/renderer/src/lib/components/workspace/WorkspaceWizard.svelte, desktop/src/renderer/src/pages/SettingsPage.svelte, desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte, e2e/tests/ide/ide.go
Adds VS Code Web to the desktop IDE selectors and exercises --ide=vscode-web in the IDE e2e spec.

Contact email updates

Layer / File(s) Summary
Contact email values
SECURITY.md, cmd/pro/start.go, desktop/package.json, pkg/driver/kubernetes/pullsecrets.go
Updates reporting, installer help text, package metadata, and pull-secret email values to dev@devsy.sh.

Sequence Diagram(s)

sequenceDiagram
  participant SetupContainerCmd
  participant VSCodeWebAsyncCmd
  participant VSCodeWeb
  SetupContainerCmd->>VSCodeWeb: Install()
  SetupContainerCmd->>VSCodeWebAsyncCmd: start `vscode-web-async` with `--setup-info`
  VSCodeWebAsyncCmd->>VSCodeWeb: InstallExtensions()
  SetupContainerCmd->>VSCodeWeb: Start()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • devsy-org/devsy#526: Updates the same dev@devsy.sh email value in related reporting and configuration locations.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding the vscode-web browser IDE via code serve-web.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@netlify

netlify Bot commented Jun 26, 2026

Copy link
Copy Markdown

Deploy Preview for devsydev canceled.

Name Link
🔨 Latest commit 06aacb0
🔍 Latest deploy log https://app.netlify.com/projects/devsydev/deploys/6a3e9dc6f8d8d80008209291

@skevetter
skevetter marked this pull request as ready for review June 26, 2026 14:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
e2e/tests/ide/ide.go (1)

46-47: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy lift

This smoke test stops short of the new browser-specific path.

With --ide-launch=skip, Lines 46-47 only prove that devsy up accepts --ide=vscode-web. The PR’s new risk is on first browser connect, where serve-web fetches its payload, so opener/tunnel wiring and the outbound-network dependency can still regress unnoticed. Add one smoke step that actually opens the IDE or hits the tunneled URL after startup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/tests/ide/ide.go` around lines 46 - 47, The smoke test in ide.go only
verifies that DevsyUpWithIDE accepts --ide=vscode-web with --ide-launch=skip,
but it never exercises the browser-specific startup path. Update the test around
DevsyUpWithIDE and the subsequent assertion to actually open the IDE or request
the tunneled serve-web URL after startup so the opener/tunnel wiring and
outbound fetch path are covered. Use the existing DevsyUpWithIDE flow in this
test to add a single post-startup browser-connect step and keep the no-error
check on that interaction.
.golangci.yaml (1)

45-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep the dupl suppression narrower than the whole file.

Excluding all of cmd/internal/agentcontainer/setup.go turns off duplicate detection for unrelated setup paths too. A per-function //nolint:dupl or a narrower exclusion keeps the intentional mirror code quiet without losing future signal elsewhere in the file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.golangci.yaml around lines 45 - 47, The current dupl suppression in the
golangci config is too broad because it disables duplicate detection for the
entire setup.go file. Narrow the suppression by replacing the file-level
exclusion with a per-function `//nolint:dupl` on the intentional mirrored code
in the relevant setup function(s), or scope the exclusion to only the specific
duplicated block so unrelated paths in `cmd/internal/agentcontainer/setup.go`
still get checked.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/internal/agentcontainer/setup.go`:
- Around line 853-860: The VSCode Web setup is ignoring the configured bind
address and always hardcoding the listener in the NewVSCodeWeb ServerOptions.
Update the setup in setup.go to read and pass through the user-supplied
BIND_ADDRESS/ideOptions value instead of fixed 0.0.0.0 and the default port, and
ensure NewVSCodeWeb receives the actual bind target so it can honor the
configured address. Use the ServerOptions construction around NewVSCodeWeb and
the BIND_ADDRESS handling in vscodeweb.go as the key places to align.

In `@pkg/ide/opener/opener.go`:
- Around line 452-457: The shared browser IDE helper is still using
browserIDESpec.DefaultPort to populate ExtraPorts, so forwarded ports can
diverge from the parsed bind port exposed by BIND_ADDRESS. Update openBrowserIDE
to derive ExtraPorts from the parsed port value obtained from the bind address,
and ensure callers like the browser IDE path continue passing through the parsed
bind settings via browserIDESpec so auto-forwarding matches the browser URL.
- Around line 460-462: The TargetURLFn URL builder is inserting folder directly
into the query string, which breaks for paths containing spaces or reserved
characters. Update the TargetURLFn implementation to construct the URL with
net/url values (or at minimum apply url.QueryEscape to folder) so the folder
parameter is safely encoded before returning the VS Code Web URL.

In `@pkg/ide/vscodeweb/vscodeweb.go`:
- Around line 124-140: The install flow in vscodeweb.go currently short-circuits
in the install path by returning as soon as binaryPath(location) exists, which
prevents re-evaluating the requested release and prevents installSettings() from
being refreshed when Values change. Update the install logic around
v.installSettings, downloadAndExtract, and the binaryPath check so it either
keys installs off the selected release/version or detects config changes before
skipping, and ensure settings.json is rewritten whenever the desired workspace
or override settings differ from what is already installed.
- Around line 225-238: Sanitize or quote the config-supplied extension ID before
logging in the extension install loop in vscodeweb.go, since logging it verbatim
can allow newline-based log forging. Update all three call sites that reference
extension in this block—log.Info, log.Errorf, and log.Infof—so they use a
quoted/sanitized representation (for example via %q or an equivalent safe
formatter) while keeping the install command behavior unchanged.

---

Nitpick comments:
In @.golangci.yaml:
- Around line 45-47: The current dupl suppression in the golangci config is too
broad because it disables duplicate detection for the entire setup.go file.
Narrow the suppression by replacing the file-level exclusion with a per-function
`//nolint:dupl` on the intentional mirrored code in the relevant setup
function(s), or scope the exclusion to only the specific duplicated block so
unrelated paths in `cmd/internal/agentcontainer/setup.go` still get checked.

In `@e2e/tests/ide/ide.go`:
- Around line 46-47: The smoke test in ide.go only verifies that DevsyUpWithIDE
accepts --ide=vscode-web with --ide-launch=skip, but it never exercises the
browser-specific startup path. Update the test around DevsyUpWithIDE and the
subsequent assertion to actually open the IDE or request the tunneled serve-web
URL after startup so the opener/tunnel wiring and outbound fetch path are
covered. Use the existing DevsyUpWithIDE flow in this test to add a single
post-startup browser-connect step and keep the no-error check on that
interaction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 247d5a4c-6fe7-4a36-81dd-979ace634af9

📥 Commits

Reviewing files that changed from the base of the PR and between 32c704a and 4c423bb.

⛔ Files ignored due to path filters (1)
  • desktop/src/renderer/public/icons/ides/vscode-web.svg is excluded by !**/*.svg
📒 Files selected for processing (19)
  • .golangci.yaml
  • SECURITY.md
  • cmd/internal/agentcontainer/container.go
  • cmd/internal/agentcontainer/setup.go
  • cmd/internal/agentcontainer/vscodeweb_async.go
  • cmd/pro/start.go
  • desktop/package.json
  • desktop/src/renderer/src/lib/components/workspace/WorkspaceWizard.svelte
  • desktop/src/renderer/src/pages/SettingsPage.svelte
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
  • e2e/tests/ide/ide.go
  • pkg/config/ide.go
  • pkg/driver/kubernetes/pullsecrets.go
  • pkg/ide/ideparse/parse.go
  • pkg/ide/opener/browser_tunnel_state.go
  • pkg/ide/opener/opener.go
  • pkg/ide/types.go
  • pkg/ide/vscodeweb/vscodeweb.go
  • pkg/ide/vscodeweb/vscodeweb_test.go

Comment on lines +853 to +860
vw := vscodeweb.NewVSCodeWeb(vscodeweb.ServerOptions{
Extensions: vsCodeConfiguration.Extensions,
Settings: settings,
UserName: user,
Host: "0.0.0.0",
Port: strconv.Itoa(vscodeweb.DefaultVSCodeWebPort),
Values: ideOptions,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Honor BIND_ADDRESS instead of hardcoding the default listener.

pkg/ide/vscodeweb/vscodeweb.go exposes BIND_ADDRESS, but this path always passes 0.0.0.0:10802, and NewVSCodeWeb only uses Values for download selection. As written, any user-supplied bind address is ignored.

Suggested fix
+	host := "0.0.0.0"
+	port := strconv.Itoa(vscodeweb.DefaultVSCodeWebPort)
+	if bindAddr := vscodeweb.Options.GetValue(ideOptions, vscodeweb.BindAddressOption); bindAddr != "" {
+		parsedHost, parsedPort, err := net.SplitHostPort(bindAddr)
+		if err != nil {
+			return fmt.Errorf("parse %s: %w", vscodeweb.BindAddressOption, err)
+		}
+		host, port = parsedHost, parsedPort
+	}
+
 	vw := vscodeweb.NewVSCodeWeb(vscodeweb.ServerOptions{
 		Extensions: vsCodeConfiguration.Extensions,
 		Settings:   settings,
 		UserName:   user,
-		Host:       "0.0.0.0",
-		Port:       strconv.Itoa(vscodeweb.DefaultVSCodeWebPort),
+		Host:       host,
+		Port:       port,
 		Values:     ideOptions,
 	})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
vw := vscodeweb.NewVSCodeWeb(vscodeweb.ServerOptions{
Extensions: vsCodeConfiguration.Extensions,
Settings: settings,
UserName: user,
Host: "0.0.0.0",
Port: strconv.Itoa(vscodeweb.DefaultVSCodeWebPort),
Values: ideOptions,
})
host := "0.0.0.0"
port := strconv.Itoa(vscodeweb.DefaultVSCodeWebPort)
if bindAddr := vscodeweb.Options.GetValue(ideOptions, vscodeweb.BindAddressOption); bindAddr != "" {
parsedHost, parsedPort, err := net.SplitHostPort(bindAddr)
if err != nil {
return fmt.Errorf("parse %s: %w", vscodeweb.BindAddressOption, err)
}
host, port = parsedHost, parsedPort
}
vw := vscodeweb.NewVSCodeWeb(vscodeweb.ServerOptions{
Extensions: vsCodeConfiguration.Extensions,
Settings: settings,
UserName: user,
Host: host,
Port: port,
Values: ideOptions,
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/internal/agentcontainer/setup.go` around lines 853 - 860, The VSCode Web
setup is ignoring the configured bind address and always hardcoding the listener
in the NewVSCodeWeb ServerOptions. Update the setup in setup.go to read and pass
through the user-supplied BIND_ADDRESS/ideOptions value instead of fixed 0.0.0.0
and the default port, and ensure NewVSCodeWeb receives the actual bind target so
it can honor the configured address. Use the ServerOptions construction around
NewVSCodeWeb and the BIND_ADDRESS handling in vscodeweb.go as the key places to
align.

Comment thread pkg/ide/opener/opener.go
Comment on lines +452 to +457
return openBrowserIDE(ctx, params, browserIDESpec{
BindAddrOption: vscodeweb.Options.GetValue(ideOptions, vscodeweb.BindAddressOption),
DefaultPort: vscodeweb.DefaultVSCodeWebPort,
ForwardPorts: vscodeweb.Options.GetValue(
ideOptions, vscodeweb.ForwardPortsOption,
) == config.BoolTrue,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the parsed bind port for forwarded ports.

This path exposes BIND_ADDRESS, but the shared openBrowserIDE helper still populates ExtraPorts from spec.DefaultPort instead of the parsed port. A config like 0.0.0.0:12345 will therefore target 12345 in the browser URL while auto-forwarding still registers 10802. Please switch the shared helper to use the parsed port before routing more IDEs through it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/ide/opener/opener.go` around lines 452 - 457, The shared browser IDE
helper is still using browserIDESpec.DefaultPort to populate ExtraPorts, so
forwarded ports can diverge from the parsed bind port exposed by BIND_ADDRESS.
Update openBrowserIDE to derive ExtraPorts from the parsed port value obtained
from the bind address, and ensure callers like the browser IDE path continue
passing through the parsed bind settings via browserIDESpec so auto-forwarding
matches the browser URL.

Comment thread pkg/ide/opener/opener.go
Comment thread pkg/ide/vscodeweb/vscodeweb.go Outdated
Comment thread pkg/ide/vscodeweb/vscodeweb.go Outdated
…row dupl

- url.QueryEscape the folder query param in browser IDE TargetURLFn so
  workspace paths with spaces/#/& open the correct folder (all 3 browser IDEs)
- log extension IDs with %q to prevent CRLF log forging from config input
- replace file-level dupl exclusion on setup.go with per-function //nolint:dupl
  on the intentionally-mirrored setupCodeServer/setupVSCodeWeb
Track the release URL used at install time in a .release marker and skip
download only when the existing install matches. settings.json is now
rewritten on every Install so workspace settings stay current after the
first install. Adds marker round-trip and reinstall-detection tests.

Addresses PR review (CodeRabbit #4).
@skevetter
skevetter merged commit b16b074 into main Jun 26, 2026
109 of 111 checks passed
@skevetter
skevetter deleted the feat/ide-vscode-web branch June 26, 2026 16:49
skevetter added a commit that referenced this pull request Jun 26, 2026
The VS Code for the Web IDE (added in #532) references
$WebsiteAssetsURL/vscode-web.svg; host the icon on assets.devsy.sh.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant