Repository navigation
feat(wsl): Windows path actions for WSL hosts through one owner-routed capability (WSL-via-orcad C8) - #27955
Conversation
cba0769 to
6168b4a
Compare
4af56c6 to
fa32932
Compare
6168b4a to
e4f518b
Compare
ec58b58 to
c6f81c8
Compare
e4f518b to
bb1fc63
Compare
c6f81c8 to
661b793
Compare
bb1fc63 to
83b1b27
Compare
661b793 to
3d1c331
Compare
83b1b27 to
54961ca
Compare
3d1c331 to
0f076fd
Compare
c167054 to
741e9ff
Compare
0afe9ca to
b8e0d00
Compare
3387656 to
c6bd817
Compare
b8e0d00 to
73b44f9
Compare
c6bd817 to
3fb1e7a
Compare
…capability (WSL-via-orcad C8)
… the Windows folder picker
… Explorer drop filled in A path left filled from an earlier pick on an SSH or paired host opens the host browser again, as it did before.
73b44f9 to
9a1bfed
Compare
|
Merge-train review (blast radius outside WSL): the remote server-path step skipped the host folder browser whenever a path was already filled. That also fired for SSH and paired hosts, for example after re-selecting the same host with a path left over from an earlier pick, which exposed stale paths without the browser's host-side lookup. The skip now applies only to a path that Windows' folder picker or an Explorer drop handed over (tracked in |
📝 Walkthrough
Priority: ➖ Normal Merge Risk: 🔵 Low · up to
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
6adbff5a-6f5c-42c4-b376-60dddbed2d60
📒 Files selected for processing (36)
src/main/ipc/shell-owned-path-target.tssrc/main/ipc/shell-wsl-owner.test.tssrc/main/ipc/shell.tssrc/main/windows-path-access-host.tssrc/main/wsl/wsl-host-service.tssrc/renderer/src/components/editor/EditorPanelHeaderPath.tsxsrc/renderer/src/components/right-sidebar/file-explorer-row-context-menu.tsxsrc/renderer/src/components/right-sidebar/source-control/listing/entry-context-menu.tsxsrc/renderer/src/components/sidebar/AddRepoDialog.tsxsrc/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsxsrc/renderer/src/components/sidebar/AddRepoDialogStepContent.tsxsrc/renderer/src/components/sidebar/AddRepoServerStartStep.tsxsrc/renderer/src/components/sidebar/WorktreeOpenInMenu.tsxsrc/renderer/src/components/sidebar/add-repo-browse-authority.test.tssrc/renderer/src/components/sidebar/add-repo-browse-authority.tssrc/renderer/src/components/sidebar/add-repo-wsl-host-folder-picker.test.tssrc/renderer/src/components/sidebar/add-repo-wsl-host-folder-picker.tssrc/renderer/src/components/sidebar/sidebar-project-drop.test.tssrc/renderer/src/components/sidebar/sidebar-project-drop.tssrc/renderer/src/components/sidebar/use-add-repo-host-selection.tssrc/renderer/src/components/sidebar/use-add-repo-wsl-host-paths.test.tsxsrc/renderer/src/components/sidebar/use-add-repo-wsl-host-paths.tssrc/renderer/src/components/sidebar/useSidebarProjectDrop.os-file-drop.test.tsxsrc/renderer/src/components/sidebar/useSidebarProjectDrop.tssrc/renderer/src/components/tab-bar/EditorFileTabContextMenu.tsxsrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/lib/external-editor-open-capability.tssrc/renderer/src/lib/local-path-open-guard.tssrc/renderer/src/lib/reveal-in-file-manager.tssrc/renderer/src/lib/windows-path-access-guards.test.tssrc/renderer/src/lib/windows-path-access.tssrc/renderer/src/store/index.tssrc/shared/shell-open-types.tssrc/shared/windows-path-access.test.tssrc/shared/windows-path-access.tstests/e2e/wsl-host-fake-transport.spec.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.
| return normalized === null ? null : toWindowsWslUncPath(normalized, distro) | ||
| }, | ||
| fromWindows: (windowsPath) => { | ||
| const parsed = parseWslUncPath(stripWin32LongPathPrefix(windowsPath.trim())) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Preserve trailing spaces in WSL paths.
If a selected UNC path ends in a space, windowsPath.trim() removes part of its final filename. For example, \\wsl.localhost\Ubuntu\repo becomes /repo instead of /repo . A folder-picker selection or Explorer drop can then add the wrong project if both folders exist. Parse the path without trimming it. Linux filenames can contain spaces. (pubs.opengroup.org)
Proposed change
--- "a/src/shared/windows-path-access.ts"
+++ "b/src/shared/windows-path-access.ts"
@@ -46,7 +46,7 @@
return normalized === null ? null : toWindowsWslUncPath(normalized, distro)
},
fromWindows: (windowsPath) => {
- const parsed = parseWslUncPath(stripWin32LongPathPrefix(windowsPath.trim()))
+ const parsed = parseWslUncPath(stripWin32LongPathPrefix(windowsPath))
// Why case-insensitive: Windows folds the distro in UNC paths (`\\wsl.localhost\ubuntu`).
if (!parsed || !sameWslDistroName(parsed.distro, distro)) {
return null📝 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.
| const parsed = parseWslUncPath(stripWin32LongPathPrefix(windowsPath.trim())) | |
| const parsed = parseWslUncPath(stripWin32LongPathPrefix(windowsPath)) |
Stacked on #27934 (C7 host UI). Do not merge before it.
ELI5
A WSL distro added as a host keeps its files inside Linux, but Windows can still reach them at
\\wsl.localhost\<distro>\.... Until now Orca treated a WSL host like any other remote server, so "Reveal in File Explorer", "Open in VS Code", dragging a folder from Explorer, and Windows' own folder picker were all off for it. This turns them back on for WSL hosts only, on Windows only.What Changed
Before: for a project on a WSL host, Reveal and Open in editor were disabled ("Local only"). Dropping any folder on the sidebar while the WSL host was focused was refused. Add Project's Browse opened only Orca's in-app browser.
After (Windows, WSL host owner):
\\wsl.localhost\<distro>\<path>.--remote wsl+<distro> <linux path>. This reuses the existing launcher logic from fix(wsl): open WSL paths in Cursor's WSL mode; ignore Windows-side CLIs in WSL hook setup #27681, which turns a WSL network path into that form. Other editors get the\\wsl.localhostpath. The Open In menu labels these entries "WSL".\\wsl.localhost\<distro>\...or\\wsl$\...folder goes to the WSL host whose distro it names, whatever host is focused. Add Project opens with that host selected and the Linux path filled in. AC:\drop is never sent to a WSL host (D7): with the WSL host focused it is refused with a "stays on this computer" message, and otherwise it behaves as before.\\wsl.localhost\<distro>\<home>. The chosen folder is turned back into its Linux path. A folder on a drive or in another distro is refused, and the message says why.Mechanism: one capability,
windowsPathAccess(ownerHostId)insrc/shared/windows-path-access.ts, returns{ toWindows, fromWindows }or null. It is non-null only when the platform is win32 and the owner host is a runtime environment backed by a WSL host record. It is keyed on the resource's owner host, never on the focused host. Main resolves it from its WSL host records (resolveWindowsPathAccess). The renderer resolves it from the known runtime environments (getWindowsPathAccess). Every guard goes through it:isRevealOrEditorOpenBlocked(the reveal call sites and the Open In menu) andgetExternalEditorOpenCapabilityshell.tsresolveOwnedPathTarget(reveal and external editor)Main translates the path again itself. It never trusts a path the renderer already translated.
All translation is plain string work on the desktop:
\\wsl.localhostand the legacy\\wsl$prefix, plus the\\?\UNC\long-path form. Distro names match without regard to case, and paths are always written with the distro name as registered (issue #23960 class). Nothing runs inside the distro.Deliberately unchanged:
\\wsl.localhostfolder is what D5 avoids.Why
The design calls for exactly one owner-routed capability (D5, risk R16). A single function means every guard can be found with one grep, and every non-WSL host (local, SSH, paired server, macOS/Linux desktops) gets null, so their behavior does not change. Converting paths on the desktop avoids WSL interop, which can be disabled or broken (the A-track issues). The alternatives were:
wsl:id checks into each guard. That repeats the rule and drifts.wslpathinside the distro. That needs interop and a round trip.Linked Issue
Fixes #
Visual Proof
Taken through CDP from hidden windows by the fake-WSL-transport e2e. The renderer is told it is Windows, and main's native dialog is stubbed.
Before / off Windows, Browse on a WSL host opens the in-app browser:

After, Windows' picker returned

\\wsl.localhost\ubuntu\...(lowercase), and the host path is filled with the Linux path:After, picking a

C:\folder is refused with D7 copy:Testing
Unit tests:
windows-path-access.test.ts: owner routing, win32-only, casing, spaces, unicode,\\wsl$,\\?\UNC,.., drive and other-distro refusals.shell-wsl-owner.test.ts: Explorer gets the UNC path; VS Code and Cursor get--remote wsl+Ubuntu /home/me/my repo; other editors get the UNC path; a paired server is still refused; system-default open is still refused.windows-path-access-guards.test.ts: renderer guards, web client and non-Windows give null.sidebar-project-drop.test.ts: drop routing.add-repo-wsl-host-folder-picker.test.tsadd-repo-browse-authority.test.tsRenderer tests:
useSidebarProjectDrop.os-file-drop.test.tsxcovers a UNC drop going to the WSL host, a drive drop refused while the WSL host is focused, and the experiment off.use-add-repo-wsl-host-paths.test.tsxcovers the picker filling the path, a stale pick after close being dropped, and a dropped path applied once its host is selected.e2e:
tests/e2e/wsl-host-fake-transport.spec.tsnow covers the Windows folder picker (a pick that lands on the host, and a drive refusal). Reveal and open in editor can't be exercised end to end off Windows, because main resolves the capability fromprocess.platform. They are covered by the main handler tests above.Real Windows host: not run. The agent session refused to send commands to the
win-lowspecterminal, so\\wsl.localhostin Explorer andcode --remote wsl+<distro>are still unverified on real hardware.Gates run locally:
pnpm tc, oxlint,pnpm format,check:code-quality:changed, React Doctor changed-lines (warnings only, none new in the gate), focus-read, layout-writer, max-lines, ts-nocheck and runtime-electron ratchets, Zustand fan-out, reliability gates, dead classes, anti-slop, native and type-aware code-quality, localization extraction, catalogs and coverage.I manually tested these changes locally
Automated tests added/updated, or explained why not below
AI Disclosure
Review
Agent skill upstream boundary
Notes
Cross-platform: everything is gated on win32 (renderer user agent, main
process.platform). macOS and Linux desktops are unchanged. SSH and paired servers resolve to null, so they keep refusing exactly as before. Wire compatibility: no RPC or stream changes; only desktop IPC was touched (shell handlers now accept a WSL owner). Folder workspaces go through the same owner path.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)