@deepseek-ai/dsh-fs-local 0.1.3-alpha.2 → 0.1.5-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/fs/fs-local/README.md
5
- README.md: 07fb96d457abd3807e892d37d473e92093078664
6
- README.zh.md: 4980de0c7fe771f75e38d896644d62c29c352c04
5
+ README.md: 427cf02631f9d56246851f7fc67c92dd662c2a62
6
+ README.zh.md: 7009d9dd853b5443780978036792f10ebc9706b5
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- `dsh-fs-local` implements the `ctx.fs` filesystem contract ([`dsh-fs`](../fs/README.md)) on the host filesystem: loading it as a plugin populates `ctx.fs` with real file access — resolve, read, list, atomic write, and literal edit against the local machine's files. Relative paths resolve from a configurable base directory, and the same file reached through different paths or symlinks shares one identity. Because this backend shares the host filesystem, it can also map an absolute host path into the process path used by this execution world. Writes are atomic and preserve file permissions; the optional version guard makes stale overwrites fail instead of clobbering. Choose it when a process needs direct, unconfined access to host files; choose `fs-sandbox` when mutations must be confined, or `fs-e2b` when file state belongs in a remote execution world.
12
+ Use `dsh-fs-local` to read, list, atomically write, and edit files on the host filesystem. Relative paths resolve from a configurable base directory, while absolute paths and parent traversal remain unrestricted. Paths and symlinks that reach the same file share one identity. Writes preserve file permissions, and optional version guards reject stale overwrites. Choose this package for direct host access; use `fs-sandbox` for confined mutations or `fs-e2b` for files in a remote execution world.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -50,7 +50,7 @@ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-a
50
50
 
51
51
  ### What you can do
52
52
 
53
- Read any regular UTF-8 text file whole or as a stream, read raw bytes up to a cap you choose, and list one directory level in stable name order. Create or replace a file atomically, and apply a literal text edit atomically; both mutations serialize per file, so concurrent writers never interleave. The version guard is optional: omit it for unconditional create-or-overwrite, or supply it to fail when the file changed since you last observed it.
53
+ Read any regular UTF-8 text file whole or as a stream, read raw bytes up to a cap you choose or as one byte window, and list one directory level in stable name order. Create or replace a file atomically, and apply a literal text edit atomically; both mutations serialize per file, so concurrent writers never interleave. The version guard is optional: omit it for unconditional create-or-overwrite, or supply it to fail when the file changed since you last observed it.
54
54
 
55
55
  Failures are typed `FsError`s 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 a guarded edit reports `FS_STALE_VERSION` either way.
56
56
 
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-fs-local` 在宿主文件系统上实现 `ctx.fs` 文件系统约定([`dsh-fs`](../fs/README.zh.md)):把它作为插件加载后,`ctx.fs` 就拥有真实的文件访问能力——针对本机文件的解析、读取、列出、原子写入与字面量编辑。相对路径从可配置的基准目录解析,经不同路径或符号链接到达的同一文件共享一个身份。由于本后端共享宿主文件系统,它还可以把绝对宿主路径映射为此执行世界使用的进程路径。写入是原子的并保留文件权限;可选版本防护让陈旧覆盖失败而不是静默覆盖。当进程需要直接、不受约束地访问宿主文件时选择它;需要约束变更时选择 `fs-sandbox`,文件状态属于远程执行世界时选择 `fs-e2b`。
12
+ 使用 `dsh-fs-local` 可在宿主文件系统上读取、列出、原子写入和编辑文件。相对路径从可配置的基准目录解析,而绝对路径和父目录遍历不受限制。到达同一文件的路径和符号链接共享一个身份。写入保留文件权限,可选版本防护会拒绝陈旧覆盖。直接访问宿主文件时选择本包;需要约束变更时使用 `fs-sandbox`,文件位于远程执行世界时使用 `fs-e2b`。
13
13
 
14
14
  ## 目录
15
15
 
@@ -50,7 +50,7 @@ kind: "package-reference"
50
50
 
51
51
  ### 你能做什么
52
52
 
53
- 完整或流式读取任意普通 UTF-8 文本文件,按你选择的上限读取原始字节,并按稳定名称顺序列出一层目录。原子地创建或替换文件,并原子地应用字面量文本编辑;两个变更操作都按文件串行化,并发写入方绝不会交错。版本防护是可选的:省略它即无条件创建或覆盖,提供它则在文件自上次观察以来发生变化时失败。
53
+ 完整或流式读取任意普通 UTF-8 文本文件,按你选择的上限或按字节窗口读取原始字节,并按稳定名称顺序列出一层目录。原子地创建或替换文件,并原子地应用字面量文本编辑;两个变更操作都按文件串行化,并发写入方绝不会交错。版本防护是可选的:省略它即无条件创建或覆盖,提供它则在文件自上次观察以来发生变化时失败。
54
54
 
55
55
  失败是携带稳定错误码的类型化 `FsError`——`FS_NOT_FOUND`、`FS_NOT_TEXT`(二进制内容)、`FS_STALE_VERSION`(自观察以来已变化)、`FS_EDIT_NOT_FOUND` 或 `FS_AMBIGUOUS_EDIT`(无唯一字面量匹配)等——因此调用方依据错误码分支,绝不解析消息文本。带防护的编辑遇到缺失目标时,无论哪种情况都报告 `FS_STALE_VERSION`。
56
56
 
package/lib/index.js CHANGED
@@ -390,6 +390,39 @@ async function readWholeBytes(target, signal, maxBytes, internals = {}) {
390
390
  return Buffer.concat(chunks, bytes);
391
391
  }
392
392
  /**
393
+ * Read the bytes at `[offset, offset + length)` of a regular file with no
394
+ * decoding or binary rejection. The window is the bound: the stream opens at
395
+ * `offset` and closes after `length` bytes, so no more than the window is ever
396
+ * buffered whatever the file's size; a window at or past the end is empty.
397
+ * @param target - the resolved file to read.
398
+ * @param range - `offset`, the 0-based first byte, and `length`, the largest byte count.
399
+ * @param signal - aborts the read (`FS_ABORTED`).
400
+ * @returns the window's bytes, at most `length` long.
401
+ */
402
+ async function readByteWindow(target, range, signal) {
403
+ await statRegularFile(target, "read", signal);
404
+ if (range.length === 0) return new Uint8Array(0);
405
+ const stream = createReadStream(target.targetKey, {
406
+ start: range.offset,
407
+ end: range.offset + range.length - 1,
408
+ ...signal ? { signal } : {}
409
+ });
410
+ const chunks = [];
411
+ let bytes = 0;
412
+ try {
413
+ for await (const chunk of stream) {
414
+ chunks.push(chunk);
415
+ bytes += chunk.length;
416
+ }
417
+ } catch (error) {
418
+ /* v8 ignore next 2 -- a mid-stream abort needs cancellation racing an active read; pre-abort is deterministic. */
419
+ if (isAbortError(error)) throw new FsError("read aborted", "FS_ABORTED");
420
+ /* v8 ignore next -- any other stream failure needs an I/O fault after a successful stat. */
421
+ throw error;
422
+ }
423
+ return Buffer.concat(chunks, bytes);
424
+ }
425
+ /**
393
426
  * Stream a whole regular UTF-8 text file as decoded text chunks. Same text
394
427
  * semantics as {@link readWholeText} (regular-file check, binary/NUL rejection,
395
428
  * cross-chunk UTF-8 decoding), but never holds the whole file in memory.
@@ -764,6 +797,12 @@ var LocalFileSystem = class extends FileSystem {
764
797
  targetKey: target.targetKey
765
798
  }, signal, maxBytes, this.internals);
766
799
  }
800
+ async readByteRange(target, range, signal) {
801
+ return readByteWindow({
802
+ displayPath: target.displayPath,
803
+ targetKey: target.targetKey
804
+ }, range, signal);
805
+ }
767
806
  async listDir(target, signal) {
768
807
  return (await listDirectory({
769
808
  displayPath: target.displayPath,
@@ -114,6 +114,20 @@ export declare function readWholeText(target: LocalTarget, signal?: AbortSignal)
114
114
  * @returns the full raw content, at most `maxBytes` long.
115
115
  */
116
116
  export declare function readWholeBytes(target: LocalTarget, signal: AbortSignal | undefined, maxBytes: number, internals?: FsIoInternals): Promise<Uint8Array>;
117
+ /**
118
+ * Read the bytes at `[offset, offset + length)` of a regular file with no
119
+ * decoding or binary rejection. The window is the bound: the stream opens at
120
+ * `offset` and closes after `length` bytes, so no more than the window is ever
121
+ * buffered whatever the file's size; a window at or past the end is empty.
122
+ * @param target - the resolved file to read.
123
+ * @param range - `offset`, the 0-based first byte, and `length`, the largest byte count.
124
+ * @param signal - aborts the read (`FS_ABORTED`).
125
+ * @returns the window's bytes, at most `length` long.
126
+ */
127
+ export declare function readByteWindow(target: LocalTarget, range: {
128
+ offset: number;
129
+ length: number;
130
+ }, signal?: AbortSignal): Promise<Uint8Array>;
117
131
  /**
118
132
  * Stream a whole regular UTF-8 text file as decoded text chunks. Same text
119
133
  * semantics as {@link readWholeText} (regular-file check, binary/NUL rejection,
@@ -53,6 +53,10 @@ export declare class LocalFileSystem extends FileSystem {
53
53
  readText(target: FsTarget, signal?: AbortSignal): Promise<string>;
54
54
  streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterable<string>>;
55
55
  readBytes(target: FsTarget, signal: AbortSignal | undefined, maxBytes: number): Promise<Uint8Array>;
56
+ readByteRange(target: FsTarget, range: {
57
+ offset: number;
58
+ length: number;
59
+ }, signal?: AbortSignal): Promise<Uint8Array>;
56
60
  listDir(target: FsTarget, signal?: AbortSignal): Promise<FsDirEntry[]>;
57
61
  writeText(target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal): Promise<FsWriteOutcome>;
58
62
  editText(target: FsTarget, edit: FsEditRequest, expected?: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-fs-local",
3
3
  "description": "Local-filesystem implementation of the DeepSeek Harness filesystem seam (ctx.fs)",
4
- "version": "0.1.3-alpha.2",
4
+ "version": "0.1.5-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,16 +27,16 @@
27
27
  ],
28
28
  "license": "MIT",
29
29
  "peerDependencies": {
30
- "@deepseek-ai/dsh-fs": "^0.1.3-alpha.2",
31
- "@deepseek-ai/cordis": "^4.0.2"
30
+ "@deepseek-ai/cordis": "^4.0.2",
31
+ "@deepseek-ai/dsh-fs": "^0.1.5-alpha.2"
32
32
  },
33
33
  "dependencies": {
34
34
  "koffi": "^3.1.0",
35
35
  "@deepseek-ai/schemastery": "^3.18.2"
36
36
  },
37
37
  "devDependencies": {
38
- "@deepseek-ai/dsh-fs": "^0.1.3-alpha.2",
39
- "@deepseek-ai/dsh-llm": "^0.1.3-alpha.2",
38
+ "@deepseek-ai/dsh-fs": "^0.1.5-alpha.2",
39
+ "@deepseek-ai/dsh-llm": "^0.1.5-alpha.2",
40
40
  "@deepseek-ai/cordis": "^4.0.2"
41
41
  }
42
42
  }