Keeps the apply_patch toolview registration with master's file-menu inject, takes master's shortened slot JSDoc, re-adds the seven filesystem type-equiv manifest entries, regenerates the event matrix and client catalogs, moves the fs-observation-policy upgrade guide under v0.2.2-alpha.2 with the matching fs-patch version, and refreshes the two SDK scenarios master added for the write and edit guidance change.
description, kind
| description | kind |
|---|---|
| The host-filesystem backend for ctx.fs for deployments and maintainers choosing or debugging local file access. | package-reference |
@deepseek-ai/dsh-fs-local
English | 中文
Summary
Use dsh-fs-local to read, list, watch, write, edit, patch, delete, and move files on the host. Relative paths use a configurable base; absolute paths and parent traversal remain unrestricted. Aliases share one identity. Optional guards reject stale versions, and text publication preserves permissions. Moves can also apply hunks and report a published destination separately from source removal. Use fs-sandbox when mutations need confinement.
Table of Contents
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
Mount this backend when a composition needs ctx.fs backed by the real host filesystem and accepts a process-local implementation. The common path is explicit: load the backend, give it a base directory, and the model-facing tools (dsh-tool-fs) or your own plugins can read, write, and edit files.
When to choose it
Choose fs-local for ordinary host-file access in a single process. Choose fs-sandbox when a session's writes and edits must be confined to its workspace and temp roots — it extends this backend and adds only the mode fence. config.cwd is a resolution default, not a containment boundary: absolute paths and .. escape it.
Minimal configuration
Load the backend with a base directory; relative paths resolve against it, and absolute paths ignore it. A requested path that is ~ or starts with ~/ (or ~\ on Windows) begins at the host user's home directory; ~user, later ~ segments, and directory entry names stay literal. A relative base is anchored to the provider process working directory, and display paths remain absolute. On POSIX, resolution follows filesystem semantics before lexical normalization: symlink/.. reaches the parent of the link target, including when the final file does not exist yet. Directory listings preserve the same physical traversal in displayed child paths. Windows retains native drive-relative normalization.
- name: '@deepseek-ai/dsh-fs-local'
config:
cwd: /absolute/path/to/workspace
| Field | Default | Meaning |
|---|---|---|
cwd |
process.cwd() |
Base directory for relative paths |
diffBasisMaxBytes |
10 MiB |
UTF-8 byte limit per overwrite-diff side; larger overwrites return before: null |
The generated configuration catalog is the exhaustive source for every accepted field and its JSDoc.
What you can do
Read regular UTF-8 files whole or as streams, read bounded bytes, and list one directory level. Writes, literal edits, patches, and deletions serialize per target. Moves lock both targets in lexical order. patchText checks the version before calling applyHunks; deleteFile removes only a regular file. Missing sources use FS_STALE_VERSION, unmatched hunks use FS_PATCH_MISMATCH, and identical move targets use FS_SAME_TARGET.
Patch and move text is LF-normalized internally and restored using the source line-ending style; outcome text uses LF. Delete bases and overwritten destination bases follow diffBasisMaxBytes. A move checks cancellation before staging the destination, then completes both effects; a source-removal failure returns the published destination with sourceDeleted: false. Atomic write failures map denied directory or staging creation to FS_PERMISSION_DENIED and other Node I/O errors to FS_IO_ERROR.
Read, listing, and mutation failures are typed FsErrors with stable codes — FS_NOT_FOUND, FS_NOT_TEXT (binary content), FS_STALE_VERSION (changed since observation), FS_EDIT_NOT_FOUND or FS_AMBIGUOUS_EDIT (no unique literal match), and others — so callers branch on the code, never on message text. A missing target on an edit reports FS_STALE_VERSION whether or not the version guard is supplied.
Chokidar observes one file or a directory's direct entries through OS events, without polling or recursive watching. Files use a filtered parent-directory watch, so readiness also covers creation of an initially missing file. File watches cover in-place writes, atomic replacement, deletion, and same-path recreation while the parent directory remains.
Understand the implementation
Implementation internals — click to expand
This section explains the design decisions behind the local backend and points at the code that realizes them; the observable behavior is fully covered in Use this package.
Design concept
The backend builds on three ideas:
- Realpath identity. The
targetKeyis the file'srealpath, so two input paths reaching the same file through symlinks share one identity, and writes land on the link target while preserving the link. - Atomic publication. Writes stage into an exclusive temp file inside a private staging directory next to the target, fsync, then publish; an existing file's mode is preserved and Windows DACLs survive replacement.
- One mutation critical section. A per-target FIFO lock serializes read→guard→write windows, so concurrent writes and edits are deterministically ordered — one wins, the rest see the new version and reject as stale.
Source map
| File | Role |
|---|---|
src/index.ts |
Service wiring: LocalFileSystem, Config, per-target mutation lock |
src/fsio.ts |
Cordis-free raw I/O: probe, reads, atomic write, literal edit, line-ending handling |
src/win32.ts |
Windows-specific DACL preservation for atomic replacement |
Write path
Each write probes the target, enforces the optional guard (createIfAbsent or replaceIfVersion), captures a bounded before diff basis when both sides are small enough, stages the new content next to the target, fsyncs, and publishes atomically. Guarded creation uses a hard-link publication that never replaces a concurrent creator, rejecting it with FS_NOT_OBSERVED instead.
Edit path
Each edit probes, verifies the version guard before literal matching (so stale edits report FS_STALE_VERSION, never a misleading no-match), reads the file, applies the literal replacement with LF normalization, restores the file's dominant line-ending style, and republishes — all inside the per-target lock.
Ownership and invariants
Raw I/O is Cordis-free and independently unit-tested in src/fsio.ts; src/index.ts stays thin wiring. config.cwd is a resolution default only — containment is the job of fs-sandbox or a tools/execute permission plugin. Cancellation is a best-effort AbortSignal checked before and after each asynchronous probe. For watches, the signal cancels initialization; after readiness, the caller must await the returned close function.
Further Exploration
Read these pages when the package-level contract is not enough. They move from the contract to the adjacent backends, tools, and policies.
- Filesystem subsystem — exhaustive provider contract, policy events, and error taxonomy.
- dsh-fs — the
ctx.fscontract this backend implements. - fs-sandbox — the sandbox-enforcing backend that extends this one.
- tool-fs — the model-facing tools that consume
ctx.fs. - fs-observation-policy — the policy plugin that guards mutations through the
fs/*events. - Windows DACL preservation note — why atomic replacement copies the target's access policy.
Model Experience
Indirectly, through dsh-tool-fs, which renders this provider's line-windowed UTF-8 content, mutation acknowledgements, and exact provider messages in capped retained results while versions, atomic-write mechanics, and directory metadata remain internal.
KV Cache effect
No direct invalidation; the named consumer owns any request-prefix changes.
Known Limitations and Deferred Work
These limits define when the local backend is a poor fit or needs special operational care. They are current package constraints, not a general filesystem comparison or a task backlog.
config.cwdis not a sandbox — it is a resolution default, not containment: absolute paths and..escape it. Enforce containment with a stricterctx.fsbackend or a permission plugin on thetools/executewaterfall.- Linux parent-directory recreation — restoring observation after a parent directory is deleted and recreated is deferred; same-path file recreation while its parent remains is supported.
- Version tokens depend on filesystem metadata — they combine device, inode, size, nanosecond mtime, and nanosecond ctime; a storage layer that cannot update any of those facts for a rewrite can still defeat the stale guard.
editTextholds the whole file (plus the edited copy) in memory — streaming exists only on the read path.- A sub-limit overwrite still buffers a contextual basis —
writeTextmay retain up to just belowconfig.diffBasisMaxBytesof prior text in addition to the caller-owned replacement; the bound does not cap the returnedaftervalue or the whole-file presentation fallback. - Binary detection is asymmetric — reads NUL-sample only the first 8192 bytes while edits scan the whole buffer, so a file with a late NUL reads fine but rejects edits.
- The per-target mutation lock is in-process only — guarded creation still uses an atomic no-replace publication across processes, but replacement writers in another process are caught only when the optional version guard observes their metadata change; they are never serialized.
- Guarded creation requires hard-link support — filesystems or mounts that reject hard-link publication cannot serve
createIfAbsent; the backend preserves the missing target and reportsFS_IO_ERROR. - Post-commit cleanup is best effort — a successful publication remains successful if removal of its owner-only staging directory fails, leaving private residue for later operator cleanup.
Dev Note
Working context for maintainers — click to expand
None.