Remove automatic moves on session activation, terminal failures, and archive restoration while preserving manual moves and worktree deletion. Replace directory listing probes with a stat-only endpoint using Node built-ins, including an isolated module-load regression test for packaged desktop. Validation: focused session, worktree, filesystem, localization, and bridge tests; workspace type-check and lint; web and VS Code builds. Desktop startup and behavior verified by the maintainer.
5.1 KiB
5.1 KiB
FS Module Documentation
Purpose
Own filesystem API behavior for the web server runtime, including workspace-bound file operations, directory listing, reveal, and background command execution jobs.
Entrypoints and structure
packages/web/server/lib/fs/routes.js: route registration and runtime-owned state for/api/fs/*endpoints.packages/web/server/lib/fs/search.js: fuzzy filesystem search runtime used by non-FS routes (for example project icon discovery).
Public exports
registerFsRoutes(app, dependencies)fromroutes.js- Registers all filesystem routes:
GET /api/fs/homePOST /api/fs/mkdirGET /api/fs/readGET /api/fs/rawGET /api/fs/statGET /api/fs/directory-statGET /api/fs/serve/:path(*)POST /api/fs/writePOST /api/fs/uploadPOST /api/fs/deletePOST /api/fs/renamePOST /api/fs/revealPOST /api/fs/execGET /api/fs/exec/:jobIdGET /api/fs/listGET /api/fs/git-dirs— shallow nested git repository discovery for the Git tab (depth- and visit-capped readdir walk;.gitdirectory, file, or symlink marks a repository boundary; junk directories and symlinks are never descended into)
- Owns exec job queue state (
execJobs) and lifecycle/TTL pruning. - Enforces workspace boundary checks with active project + worktree fallback support.
- The active project directory is validated with
fs.realpath, so when the project root is itself a symlink the workspace base no longer matches the paths the client sends. Workspace resolution therefore retries against the raw directory the client requested (requestedDirectoryfromresolveProjectDirectory) before falling back to worktree roots. Symlinks are still resolved afterwards, and write/exec routes keep their canonical containment check against the resolved base.
- Registers all filesystem routes:
createFsSearchRuntime({ fsPromises, path, spawn, resolveGitBinaryForSpawn })fromsearch.js- Returns
{ searchFilesystemFiles(rootPath, options) }. - Supports fuzzy matching, hidden-file handling, and optional
git check-ignorefiltering.
- Returns
Composition contract with index.js
index.jsprovides composition-time dependencies only (platform primitives + callbacks such asresolveProjectDirectory,normalizeDirectoryPath, andbuildAugmentedPath).index.jsno longer owns FS route handlers or FS exec job state.
Notes for contributors
- Keep filesystem policy (workspace root checks, error mapping, exec timeout behavior) inside this module, not in the composition root.
- Workspace checks accept, besides the active workspace and its worktrees, the managed roots: the OpenChamber config root and the managed chats root (
managedChatsRootdependency;OPENCHAMBER_CHATS_DIRupstream, default<config root>/chats). Chat worktrees may legitimately live outside every project workspace. GET /api/fs/homeanswers{ home, chatsRoot }.chatsRootis the server-resolved managed chats root; clients must use it instead of joininghome+ the well-known segment (a relocated root does not contain that segment).- Filesystem
EPERM/EACCESfailures use the stablereason: "os-permission"response marker. Policy denials such as workspace-boundary or missing-grant failures must not use that marker because a native folder picker cannot remediate them. GET /api/fs/directory-stat?path=...uses onestatwithout listing contents or resolving project topology. It follows the same authenticated directory-discovery path policy as/api/fs/list, including targets outside the active workspace. A directory returns{ isDirectory: true };ENOENTreturnsnot-found, and a file orENOTDIRreturnsnot-directory. Permission and other failures remain distinct from a missing path. VS Code explicitly returns 501, so the shared client treats its probe as unknown.- Read-only routes authorize the requested path against the workspace before resolving symlinks. A symlink reached through the workspace may therefore target a file outside it, while a directly requested outside path still requires an exact-path grant. Write routes keep canonical-target boundary checks.
- If adding new
/api/fs/*endpoints, add them inroutes.jsand extend this document. GET /api/fs/listmay resolve symlinks withrealpathto read directory contents, but the responsepathand each entrypathmust stay in the caller's requested path space (path.join(requestedPath, name)). Returning real paths breaks file-tree expansion for directories reached through workspace symlinks.POST /api/fs/uploadaccepts oneapplication/octet-streambody withpathand optionaloverwrite=truequery parameters. The body streams into a same-directory temp file with a 100 MiB default cap configurable throughOPENCHAMBER_FS_UPLOAD_MAX_BYTES; failed and oversized uploads clean up that temp file. New files commit through an atomic no-replace link, existing files return409unless overwrite is explicit, directory targets are rejected, and the destination parent resolves before writing so uploads cannot escape through workspace symlinks.