feat(file): add GET /api/fs/browse shallow host-file picker#235
Merged
Conversation
Restores the WebUI host-file browsing endpoint removed during M6 by porting the pre-M6 Express directoryApi.ts to Rust. Unlike /api/fs/dir (workspace tree) this is a shallow lister with navigation hints (can_go_up, parent_path) and a __ROOT__ sentinel for the Windows drive picker. Allowed roots widen to cwd + home + Windows drives + "/" on Unix because the host-file picker legitimately needs to reach outside any single workspace. Wired through FileRouterState.browse_roots so the allow-list can be injected in tests. Unblocks the DirectorySelectionModal in WebUI mode which was hitting 404s against the legacy URL.
piorpua
pushed a commit
that referenced
this pull request
May 12, 2026
Collapse nested if-let into a single condition with `&&` to satisfy clippy::collapsible_if (introduced in #235).
zhuqingyv
pushed a commit
that referenced
this pull request
May 12, 2026
Collapse nested if-let into a single condition with `&&` to satisfy clippy::collapsible_if (introduced in #235).
piorpua
pushed a commit
that referenced
this pull request
Jun 11, 2026
) ## Problem iOfficeAI/AionUi#3191 — on Windows, selecting a directory in the WebUI picker stores paths like `\\?\C:\DEV\xxxxx` as the conversation workspace. Claude Code then fails to connect (cmd.exe-based CLI shims reject verbatim working directories), and the same directory shows up as two different projects in the sidebar (`C:\DEV\xxxxx` from old sessions vs `\\?\C:\DEV\xxxxx` from new ones). ## Root cause The `/api/fs/browse` endpoint (added in #235, replacing the legacy Express implementation in AionUi) resolves paths with `fs::canonicalize`, which on Windows returns extended-length (verbatim) paths — `\\?\C:\DEV`. Those strings were serialized verbatim into `current_path`, `parent_path` and every item `path`. The legacy Express endpoint built paths with Node's `path.join` and never produced this form, so this is an M6-cutover regression. ## Fix Strip `\\?\` (and `\\?\UNC\` → `\\`) at the response-serialization boundary in `browse.rs`. Internal sandbox checks keep comparing canonical (verbatim) forms, so the allow-list semantics are unchanged. ## Tests - Cross-platform string-level unit tests for the strip helper (covered by Linux CI) - `#[cfg(windows)]` end-to-end regression test asserting no response path carries the verbatim prefix (fails on Windows without this fix) - `cargo test` full workspace: 6022 passed; clippy `-D warnings` + fmt clean Frontend defensive counterpart: iOfficeAI/AionUi PR (renderer-side normalization for older backend binaries) follows. Fixes iOfficeAI/AionUi#3191
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Why a separate route instead of reusing `/api/fs/dir`?
`/api/fs/dir` returns a recursive tree scoped to a workspace root and requires a root param. The host-file picker is fundamentally different: it walks one level at a time, needs parent/up-hints for navigation, and must reach outside any workspace (including drive letters on Windows). Overloading the existing route would weaken its invariants; a new route keeps workspace semantics intact.
Frontend PR
to be linked — will point to the AionUi repo PR that flips `DirectorySelectionModal` from `/api/directory/browse` to `/api/fs/browse`.
Test plan