Files
deepseek-harness/website/zh-CN/api/harness/fs.md
lintianle fcd9d8c391 website: fix nine review findings (generator coverage, loader facts, mode semantics)
Generator (all four structural gaps):
- harness service pages now render public properties/accessors, not just
  methods (ctx.codeRuntime.language/isolation were missing);
- the class page merges the same-named interface half, so ctx.root/baseUrl/
  events/logger/reflect/registry appear on Context (vendor root JSDoc gains
  prose alongside @experimental);
- Pick<…> heritage on a Context merge resolves to the picked class members,
  giving ctx.effect a documented signature on the Fiber page;
- {@link} tags normalize to code spans; merge sections get their own h2 so
  reflect members no longer nest under 'Static members'.

verify-website-yaml: reject the unloadable 'group:' pseudo-name (tree.import
only special-cases 'cordis:'; no builtin is registered here) and recurse into
@cordisjs/plugin-group nested entry lists instead.

Prose corrected against loader/cordis source: service.md isolation example
uses the real group plugin + group: true + the required isolate map;
config.md documents concurrent entry startup (Promise.all; order via inject)
and the real hmr defaults (root ['.'], base/ignored/debounce); events.md
fixes emit (synchronous, not parallel), bail (null/false also delegate), and
serial (stops at the first bail value).
2026-07-16 21:15:44 +08:00

7.0 KiB

ctx.fs

FileSystem (abstract seam) — provided by @deepseek-ai/dsh-fs.

Abstract filesystem provider service. Subclass, implement the seven storage primitives, and load the subclass as a plugin — it registers as ctx.fs (one implementation per context; loading a second throws, cordis' standard duplicate-service behavior). Semantics every backend must honor:

  • resolve returns a stable FsTarget; the same underlying file reached by different input paths must yield the same targetKey so stale guards and target lookup agree across paths (e.g. through symlinks).
  • stat returns FsInfo metadata (never content) or undefined when the target is absent.
  • readText/streamText read the whole regular text file (the stream for large files); both own regular-file checks, UTF-8 decoding, binary/NUL rejection, and FS_NOT_TEXT.
  • listDir returns direct children of a directory in stable name order with resolved child targets and cheap metadata only. It never reads file contents. Missing targets throw FS_NOT_FOUND, non-directories throw FS_NOT_DIRECTORY, permission failures throw FS_PERMISSION_DENIED, and other backend I/O failures throw FS_IO_ERROR.
  • writeText is atomic temp-file + rename. expected is OPTIONAL: omit it for an unconditional create-or-overwrite (the bare-provider default), or supply a FsWriteIntent to guard the write.
  • editText verifies expected.version BEFORE literal matching (so a stale edit reports FS_STALE_VERSION, not FS_EDIT_NOT_FOUND/ FS_AMBIGUOUS_EDIT against newer content), then applies literal replacement and writes atomically — all inside one mutation critical section. expected is OPTIONAL: omit it for an unconditional edit of the current content (a missing target still reports FS_STALE_VERSION).

Source

ctx.fs.resolve(path, opts?)

abstract resolve(path: string, opts?: { cwd?: string }): Promise<FsTarget>

Resolve a model/plugin-supplied path into a stable FsTarget. May perform I/O (a remote/sandboxed backend may need a round-trip to map a path to a stable identity), hence async even though the local backend only normalizes + realpaths. opts.cwd is the base directory a RELATIVE path resolves against; an absolute path ignores it. Omitted ⇒ the backend's own default base (the local backend uses its configured cwd). The CALLER supplies this — the seam does not read a session or agent — so a tool can resolve against the caller's per-session workspace (exec.agent.session.header.cwd) without the provider depending on dsh-agent/dsh-session. Mirrors how dsh-tool-bash defaults a bash workdir to the session cwd.

  • path — the path to resolve; relative paths resolve against opts.cwd.
  • opts — cwd overrides the backend's default base for relative paths.

Returns the stable target; the same file yields the same targetKey.

Source

ctx.fs.stat(target, signal?)

abstract stat(target: FsTarget, signal?: AbortSignal): Promise<FsInfo | undefined>

Return target metadata, or undefined when the target does not exist.

  • target — the resolved target to stat.
  • signal — aborts the metadata round-trip.

Returns metadata only, never content; undefined for an absent target.

Source

ctx.fs.readText(target, signal?)

abstract readText(target: FsTarget, signal?: AbortSignal): Promise<string>

Read the whole regular text file as a single decoded string.

  • target — the resolved target to read.
  • signal — aborts the read.

Returns the full decoded UTF-8 content.

Source

ctx.fs.streamText(target, signal?)

abstract streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterable<string>>

Stream the whole regular text file as decoded text chunks (same text semantics as readText, for large files). The backend owns cross-chunk UTF-8 decoding and binary rejection so the policy layer never touches raw bytes.

  • target — the resolved target to read.
  • signal — aborts the stream, including between chunks.

Returns the chunk iterable, decoded and validated like readText.

Source

ctx.fs.listDir(target, signal?)

abstract listDir(target: FsTarget, signal?: AbortSignal): Promise<FsDirEntry[]>

List direct children of a directory in stable name order. Returns resolved child targets plus cheap metadata only; never reads file contents.

  • target — the resolved directory target.
  • signal — aborts the listing.

Returns one entry per direct child, in stable name order.

Source

ctx.fs.writeText(target, content, expected?, signal?)

abstract writeText(target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal): Promise<FsWriteOutcome>

Create or fully replace a UTF-8 text file atomically. expected is the create-vs-replace decision and stale guard when supplied; OMITTING it is an unconditional create-or-overwrite (the bare provider — no version guard, no read-first requirement). Atomic either way.

  • target — the resolved target to write.
  • content — the full new file content.
  • expected — the write intent guarding the write; omit for unconditional.
  • signal — aborts before the atomic rename takes effect.

Returns the outcome, including the version the write produced.

Source

ctx.fs.editText(target, edit, expected?, signal?)

abstract editText(target: FsTarget, edit: FsEditRequest, expected?: { version: FsVersion }, signal?: AbortSignal): Promise<FsEditOutcome>

Apply a literal edit to an existing UTF-8 text file. When expected is supplied, verifies expected.version as the stale guard BEFORE literal matching; OMITTING it edits the current content unconditionally (no version guard). Either way applies the replacement and writes atomically — one mutation critical section — and a missing target reports FS_STALE_VERSION.

  • target — the resolved target to edit.
  • edit — the literal search/replace request.
  • expected — the version guard; omit for an unconditional edit.
  • signal — aborts before the atomic rename takes effect.

Returns the outcome, including the version the edit produced.

Source