@deepseek-ai/dsh-fs-ssh 0.1.6-alpha.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/ssh/fs-ssh/README.md
5
+ README.md: 1f403796792611abc438f140356e442fbb357cde
6
+ README.zh.md: 7e77afe6c87ed7725dea87215c9a3647d650ae95
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ ---
2
+ description: "Remote filesystem semantics for consumers sharing files with SSH subprocesses."
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-fs-ssh
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ `dsh-fs-ssh` provides `ctx.fs` in the SSH helper’s filesystem. File tools read and mutate the same files that remote Bash, terminals, language servers and Node programs see. Remote canonicalization, version guards and atomic mutations use the local filesystem implementations installed beside the helper.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Mount this provider with [`dsh-ssh`](../ssh/README.md) and `sandboxPolicy`; use its paired SSH subprocess and sandbox providers for execution. This provider has no configuration fields: connection identity and the default workspace belong to `dsh-ssh`, while file-effect mode belongs to `sandboxPolicy`.
29
+
30
+ `resolve()` canonicalizes paths on the remote host. `processPath()` and `fileUrl()` name files in that same remote namespace; they do not grant host-side access. File URLs encode literal percent signs, backslashes and newlines without changing the filename. `processPathFromHostPath()` returns `undefined`, so consumers requiring an installed executable or bootstrap must supply a remote artifact explicitly.
31
+
32
+ Reads preserve the shared filesystem error codes. Writes and edits send the resolved per-call policy to the helper, which canonicalizes the workspace and enforces it beside the atomic mutation. Lost transport reports an I/O failure; a mutation may already have committed and is not retried automatically.
33
+
34
+ -----
35
+
36
+ <a id="understand-the-implementation"></a>
37
+ ## Understand the implementation
38
+
39
+ <details>
40
+ <summary>Implementation internals — click to expand</summary>
41
+
42
+ The helper reuses [`fs-local`](../../fs/fs-local/README.md) and [`fs-sandbox`](../../fs/fs-sandbox/README.md), preserving symlink identity, stale-version rejection, diff bases and publication semantics. UTF-8 streaming uses bounded pull requests and closes its remote iterator when the consumer stops early.
43
+
44
+ </details>
45
+
46
+ -----
47
+
48
+ <a id="further-exploration"></a>
49
+ ## Further Exploration
50
+
51
+ - [Filesystem subsystem](../../../docs/subsystems/filesystem.md) — shared operations and error meanings.
52
+ - [SSH connection](../ssh/README.md) — deployment and disconnection behavior.
53
+
54
+ -----
55
+
56
+ <a id="model-experience"></a>
57
+ ## Model Experience
58
+
59
+ Indirectly, through existing filesystem consumers, which present remote paths and file contents while owning every tool and prompt.
60
+
61
+ #### KV Cache effect
62
+
63
+ This provider contributes no request-prefix content. Its consumers own model-visible tools and results.
64
+
65
+ ## Known Limitations and Deferred Work
66
+
67
+ <a id="known-limitations-and-deferred-work"></a>
68
+
69
+ - Whole-text reads and individual byte windows are limited to 8 MiB by the helper. Use text streaming or multiple byte windows for larger reads; other JSON transfers also obey the connection’s frame cap.
70
+ - Remote file URLs are execution coordinates, not host filesystem handles or Web download links.
71
+
72
+ <a id="dev-note"></a>
73
+ ### Dev Note
74
+
75
+ <details>
76
+ <summary>Working context for maintainers — click to expand</summary>
77
+
78
+ No invariant companion is published. Wire validation and the owning filesystem, subprocess and sandbox providers enforce the observable obligations; this adapter adds no independently observed state relation.
79
+
80
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,80 @@
1
+ ---
2
+ description: "面向与 SSH 子进程共享文件的消费方,说明远端文件系统语义。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-fs-ssh
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ `dsh-fs-ssh` 在 SSH 辅助进程的文件系统中提供 `ctx.fs`。文件工具读写的文件与远端 Bash、终端、语言服务器和 Node 程序看到的文件一致。远端路径规范化、版本保护及原子修改使用辅助程序旁安装的本地文件系统实现。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延后工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 将本提供方与 [`dsh-ssh`](../ssh/README.zh.md) 及 `sandboxPolicy` 一同挂载,并使用配套 SSH 子进程与沙箱提供方执行程序。本提供方没有配置字段:连接身份和默认工作区属于 `dsh-ssh`,文件效果模式属于 `sandboxPolicy`。
29
+
30
+ `resolve()` 在远端主机上规范化路径。`processPath()` 与 `fileUrl()` 在同一个远端命名空间中标识文件,并不授予主机侧访问能力。文件 URL 对字面的百分号、反斜杠和换行进行编码,保留原文件名。`processPathFromHostPath()` 返回 `undefined`,因此需要已安装可执行文件或引导程序的消费方必须显式提供远端产物。
31
+
32
+ 读取保留共享文件系统错误码。写入和编辑将已解析的逐次调用策略发送给辅助程序,由其规范化工作区并在原子修改所在位置执行策略。传输丢失报告 I/O 失败;修改可能已经提交,不会自动重试。
33
+
34
+ -----
35
+
36
+ <a id="understand-the-implementation"></a>
37
+ ## 理解实现
38
+
39
+ <details>
40
+ <summary>实现细节 — 点击展开</summary>
41
+
42
+ 辅助程序复用 [`fs-local`](../../fs/fs-local/README.zh.md) 与 [`fs-sandbox`](../../fs/fs-sandbox/README.zh.md),保留符号链接身份、陈旧版本拒绝、差异基线及发布语义。UTF-8 流使用有界拉取请求,在消费方提前停止时关闭远端迭代器。
43
+
44
+ </details>
45
+
46
+ -----
47
+
48
+ <a id="further-exploration"></a>
49
+ ## 进一步探索
50
+
51
+ - [文件系统子系统](../../../docs/subsystems/filesystem.zh.md) — 共享操作及错误含义。
52
+ - [SSH 连接](../ssh/README.zh.md) — 部署与断连行为。
53
+
54
+ -----
55
+
56
+ <a id="model-experience"></a>
57
+ ## 模型体验
58
+
59
+ 间接通过现有文件系统消费方产生影响,由它们展示远端路径及文件内容,并负责每个工具与提示。
60
+
61
+ #### KV Cache 影响
62
+
63
+ 本提供方不贡献请求前缀内容。面向模型的工具与结果由消费方负责。
64
+
65
+ ## 已知限制与延后工作
66
+
67
+ <a id="known-limitations-and-deferred-work"></a>
68
+
69
+ - 辅助程序将整段文本读取及单次字节窗口限制为 8 MiB。更大的读取需使用文本流或多个字节窗口;其他 JSON 传输也受连接消息大小上限约束。
70
+ - 远端文件 URL 是执行坐标,不是主机文件系统句柄或 Web 下载链接。
71
+
72
+ <a id="dev-note"></a>
73
+ ### 开发备注
74
+
75
+ <details>
76
+ <summary>维护者工作上下文 — 点击展开</summary>
77
+
78
+ 本包不发布不变式伴随入口。消息校验及所属文件系统、子进程和沙箱提供方执行可观察的约束;此适配器没有增加可独立观察的状态关系。
79
+
80
+ </details>
package/lib/index.js ADDED
@@ -0,0 +1,121 @@
1
+ import { posix } from "node:path";
2
+ import { pathToFileURL } from "node:url";
3
+ import { FileSystem, FsError } from "@deepseek-ai/dsh-fs";
4
+ import { RemoteOperationError } from "@deepseek-ai/dsh-ssh/protocol";
5
+ import { editResultSchema, entriesSchema, infoSchema, pathInfoSchema, targetSchema, textStreamIdSchema, writeResultSchema } from "@deepseek-ai/dsh-ssh/schemas";
6
+ import { z } from "zod";
7
+ //#region lib/types/index.js
8
+ /** Filesystem provider preserving remote identities and helper-owned atomic mutations. */
9
+ const errorCodes = {
10
+ FS_NOT_FOUND: true,
11
+ FS_NOT_DIRECTORY: true,
12
+ FS_NOT_TEXT: true,
13
+ FS_NOT_REGULAR_FILE: true,
14
+ FS_TOO_LARGE: true,
15
+ FS_PERMISSION_DENIED: true,
16
+ FS_SANDBOX_DENIED: true,
17
+ FS_IO_ERROR: true,
18
+ FS_STALE_VERSION: true,
19
+ FS_NOT_OBSERVED: true,
20
+ FS_AMBIGUOUS_EDIT: true,
21
+ FS_EDIT_NOT_FOUND: true,
22
+ FS_ABORTED: true
23
+ };
24
+ /** Remote filesystem paired with the SSH subprocess and sandbox providers. */
25
+ var SshFileSystem = class extends FileSystem {
26
+ static inject = ["ssh", "sandboxPolicy"];
27
+ get sandboxMode() {
28
+ return this.ctx.sandboxPolicy.defaultMode;
29
+ }
30
+ async resolve(path, opts) {
31
+ return await this.call("fs.resolve", {
32
+ path,
33
+ cwd: opts?.cwd
34
+ }, targetSchema, opts?.signal);
35
+ }
36
+ processPath(target) {
37
+ return String(target.targetKey);
38
+ }
39
+ fileUrl(target) {
40
+ return pathToFileURL(this.processPath(target)).href;
41
+ }
42
+ contains(parent, child) {
43
+ const path = posix.relative(this.processPath(parent), this.processPath(child));
44
+ return path === "" || !path.startsWith("../") && path !== ".." && !posix.isAbsolute(path);
45
+ }
46
+ async stat(target, signal) {
47
+ return await this.call("fs.stat", { target }, infoSchema.nullable(), signal) ?? void 0;
48
+ }
49
+ async lstat(path, opts, signal) {
50
+ return await this.call("fs.lstat", {
51
+ path,
52
+ cwd: opts?.cwd
53
+ }, pathInfoSchema.nullable(), signal) ?? void 0;
54
+ }
55
+ readText(target, signal) {
56
+ return this.call("fs.readText", { target }, z.string(), signal);
57
+ }
58
+ async streamText(target, signal) {
59
+ const id = await this.call("fs.stream", { target }, textStreamIdSchema, signal);
60
+ const call = this.call.bind(this);
61
+ return (async function* () {
62
+ let ended = false;
63
+ try {
64
+ while (!ended) {
65
+ signal?.throwIfAborted();
66
+ const next = await call("fs.next", { id }, z.object({
67
+ done: z.boolean(),
68
+ value: z.string()
69
+ }).strict(), signal);
70
+ ended = next.done;
71
+ if (next.value.length > 0) yield next.value;
72
+ }
73
+ } finally {
74
+ if (!ended) await call("fs.streamClose", { id }, z.null()).catch(() => {});
75
+ }
76
+ })();
77
+ }
78
+ async readBytes(target, signal, maxBytes) {
79
+ return Buffer.from(await this.call("fs.readBytes", {
80
+ target,
81
+ maxBytes
82
+ }, z.base64(), signal), "base64");
83
+ }
84
+ async readByteRange(target, range, signal) {
85
+ return Buffer.from(await this.call("fs.readRange", {
86
+ target,
87
+ ...range
88
+ }, z.base64(), signal), "base64");
89
+ }
90
+ async listDir(target, signal) {
91
+ return await this.call("fs.list", { target }, entriesSchema, signal);
92
+ }
93
+ async writeText(target, content, expected, signal, sandboxPolicy) {
94
+ const policy = sandboxPolicy ?? this.ctx.sandboxPolicy.resolve();
95
+ return await this.call("fs.write", {
96
+ target,
97
+ content,
98
+ expected,
99
+ policy
100
+ }, writeResultSchema, signal);
101
+ }
102
+ async editText(target, edit, expected, signal, sandboxPolicy) {
103
+ const policy = sandboxPolicy ?? this.ctx.sandboxPolicy.resolve();
104
+ return await this.call("fs.edit", {
105
+ target,
106
+ edit,
107
+ expected,
108
+ policy
109
+ }, editResultSchema, signal);
110
+ }
111
+ async call(method, params, schema, signal) {
112
+ try {
113
+ return await this.ctx.ssh.request(method, params, schema, signal);
114
+ } catch (error) {
115
+ if (error instanceof RemoteOperationError && error.code !== void 0 && Object.hasOwn(errorCodes, error.code)) throw new FsError(error.message, error.code, { cause: error });
116
+ throw new FsError(error instanceof Error ? error.message : String(error), signal?.aborted ? "FS_ABORTED" : "FS_IO_ERROR", { cause: error });
117
+ }
118
+ }
119
+ };
120
+ //#endregion
121
+ export { SshFileSystem, SshFileSystem as default };
@@ -0,0 +1,34 @@
1
+ import { FileSystem } from '@deepseek-ai/dsh-fs';
2
+ import type { FsDirEntry, FsEditOutcome, FsEditRequest, FsInfo, FsPathInfo, FsTarget, FsVersion, FsWriteIntent, FsWriteOutcome } from '@deepseek-ai/dsh-fs';
3
+ import type { SandboxExecutionPolicy, SandboxMode } from '@deepseek-ai/dsh-sandbox';
4
+ /** Remote filesystem paired with the SSH subprocess and sandbox providers. */
5
+ export declare class SshFileSystem extends FileSystem {
6
+ static inject: string[];
7
+ get sandboxMode(): SandboxMode;
8
+ resolve(path: string, opts?: {
9
+ cwd?: string;
10
+ signal?: AbortSignal;
11
+ }): Promise<FsTarget>;
12
+ processPath(target: FsTarget): string;
13
+ fileUrl(target: FsTarget): string;
14
+ contains(parent: FsTarget, child: FsTarget): boolean;
15
+ stat(target: FsTarget, signal?: AbortSignal): Promise<FsInfo | undefined>;
16
+ lstat(path: string, opts?: {
17
+ cwd?: string;
18
+ }, signal?: AbortSignal): Promise<FsPathInfo | undefined>;
19
+ readText(target: FsTarget, signal?: AbortSignal): Promise<string>;
20
+ streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterable<string>>;
21
+ readBytes(target: FsTarget, signal: AbortSignal | undefined, maxBytes: number): Promise<Uint8Array>;
22
+ readByteRange(target: FsTarget, range: {
23
+ offset: number;
24
+ length: number;
25
+ }, signal?: AbortSignal): Promise<Uint8Array>;
26
+ listDir(target: FsTarget, signal?: AbortSignal): Promise<FsDirEntry[]>;
27
+ writeText(target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal, sandboxPolicy?: SandboxExecutionPolicy): Promise<FsWriteOutcome>;
28
+ editText(target: FsTarget, edit: FsEditRequest, expected?: {
29
+ version: FsVersion;
30
+ }, signal?: AbortSignal, sandboxPolicy?: SandboxExecutionPolicy): Promise<FsEditOutcome>;
31
+ private call;
32
+ }
33
+ export default SshFileSystem;
34
+ //# sourceMappingURL=index.d.ts.map
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@deepseek-ai/dsh-fs-ssh",
3
+ "description": "Filesystem provider over the shared POSIX SSH helper",
4
+ "version": "0.1.6-alpha.1",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/ssh/fs-ssh"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./src/*": "./src/*",
22
+ "./package.json": "./package.json"
23
+ },
24
+ "files": [
25
+ "lib/index.js",
26
+ "lib/types/**/*.d.ts"
27
+ ],
28
+ "license": "MIT",
29
+ "peerDependencies": {
30
+ "@deepseek-ai/dsh-ssh": "^0.1.6-alpha.1",
31
+ "@deepseek-ai/cordis": "^4.0.2",
32
+ "@deepseek-ai/dsh-fs": "^0.1.6-alpha.1",
33
+ "@deepseek-ai/dsh-sandbox": "^0.1.6-alpha.1",
34
+ "@deepseek-ai/dsh-sandbox-policy": "^0.1.6-alpha.1"
35
+ },
36
+ "dependencies": {
37
+ "zod": "^4.4.3",
38
+ "@deepseek-ai/schemastery": "^3.18.2"
39
+ },
40
+ "devDependencies": {
41
+ "@deepseek-ai/cordis": "^4.0.2",
42
+ "@deepseek-ai/dsh-ssh": "^0.1.6-alpha.1",
43
+ "@deepseek-ai/dsh-fs": "^0.1.6-alpha.1",
44
+ "@deepseek-ai/dsh-sandbox": "^0.1.6-alpha.1",
45
+ "@deepseek-ai/dsh-sandbox-policy": "^0.1.6-alpha.1"
46
+ }
47
+ }