dsh-over-ssh 0.0.0-stage → 0.1.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -2
  3. package/agents/agent-manifest.json +20 -0
  4. package/agents/linux-amd64/dsh-over-ssh-agent +0 -0
  5. package/agents/linux-arm64/dsh-over-ssh-agent +0 -0
  6. package/cordis.patch.yml +84 -0
  7. package/lib/client.js +6660 -0
  8. package/lib/file-references.js +411 -0
  9. package/lib/fs.js +1451 -0
  10. package/lib/index.js +19740 -0
  11. package/lib/local-bridge-refs.js +16 -0
  12. package/lib/local-bridge.js +16 -0
  13. package/lib/terminal-wrapper.js +299 -0
  14. package/lib/types/src/client/index.d.ts +49 -0
  15. package/lib/types/src/config.d.ts +383 -0
  16. package/lib/types/src/file-references.d.ts +24 -0
  17. package/lib/types/src/host/app/backend.d.ts +178 -0
  18. package/lib/types/src/host/app/recycle-bin/recycle-bin-contract.d.ts +246 -0
  19. package/lib/types/src/host/app/recycle-bin/recycle-bin-service.d.ts +44 -0
  20. package/lib/types/src/host/app/session-admin/session-admin-contract.d.ts +232 -0
  21. package/lib/types/src/host/app/session-admin/session-admin-service.d.ts +39 -0
  22. package/lib/types/src/host/app/transport/transport-selector.d.ts +97 -0
  23. package/lib/types/src/host/app/transport/transport-service.d.ts +81 -0
  24. package/lib/types/src/host/domain/capability.d.ts +77 -0
  25. package/lib/types/src/host/domain/errors.d.ts +54 -0
  26. package/lib/types/src/host/domain/roots/connection.d.ts +105 -0
  27. package/lib/types/src/host/domain/roots/root.d.ts +123 -0
  28. package/lib/types/src/host/domain/roots/session-roots.d.ts +150 -0
  29. package/lib/types/src/host/domain/roots/ssh-config.d.ts +72 -0
  30. package/lib/types/src/host/domain/session/session-admin.d.ts +344 -0
  31. package/lib/types/src/host/domain/transfer/recycle-bin.d.ts +336 -0
  32. package/lib/types/src/host/domain/transfer/transfer.d.ts +259 -0
  33. package/lib/types/src/host/infra/transport/agent/agent-installer.d.ts +120 -0
  34. package/lib/types/src/host/infra/transport/ssh/ssh2-transport.d.ts +327 -0
  35. package/lib/types/src/host/infra/transport/transport-base.d.ts +33 -0
  36. package/lib/types/src/host/infra/transport/transport.d.ts +541 -0
  37. package/lib/types/src/host/integration/agent/agent-preflight.d.ts +55 -0
  38. package/lib/types/src/host/integration/fs/file-reference-provider.d.ts +141 -0
  39. package/lib/types/src/host/integration/fs/fs-provider.d.ts +366 -0
  40. package/lib/types/src/host/integration/transfer/transfer-service.d.ts +211 -0
  41. package/lib/types/src/index.d.ts +88 -0
  42. package/lib/types/src/local-bridge-refs.d.ts +25 -0
  43. package/lib/types/src/local-bridge.d.ts +35 -0
  44. package/lib/types/src/shared/format.d.ts +13 -0
  45. package/lib/types/src/shared/root-keys.d.ts +120 -0
  46. package/lib/types/src/wiring/helpers.d.ts +143 -0
  47. package/package.json +122 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-over-ssh contributors
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.
package/README.md CHANGED
@@ -1,3 +1,120 @@
1
- # Temporary Holding Version
1
+ # dsh-over-ssh
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ DSH(DeepSeek Harness)第三方插件:把 VSCode Remote-SSH 式的远程开发做进 DSH —— 连接管理(密钥 / 密码 / ProxyJump / `~/.ssh/config` 导入)、远程文件(**无同步**直读直写)、远程终端、远程工作区、远端 `@` 补全、上传/下载,并支持"本地根 + 远端根"同处一个工作区。
4
+
5
+ 设计依据:`doc/01-product/02-技术设计-SSH远程开发插件.md`(v0.2)。需求依据:`doc/01-product/01-需求分析-多根工作区-远程根-会话管理.md`(v0.4)。文档知识库入口:`doc/README.md`。
6
+
7
+ ## 安装
8
+
9
+ 从 npm(发布后):
10
+
11
+ ```bash
12
+ dsh plugin --profile web add dsh-over-ssh # 等价于在该 profile 目录里 pnpm add
13
+ ```
14
+
15
+ 从源码(本仓库开发时):
16
+
17
+ ```bash
18
+ npm i && npm run build
19
+ dsh plugin --profile web add /绝对路径/dsh-over-ssh
20
+ ```
21
+
22
+ **远端终端**要额外配一次 `terminalController.Config.shell`——`Config.shell` 是静态单值,连哪台只能由 wrapper 运行时决定:
23
+
24
+ | 装法 | 指向 |
25
+ |---|---|
26
+ | npm | `node_modules/dsh-over-ssh/lib/terminal-wrapper.js`(单文件,内部模块已内联) |
27
+ | 源码 | `tools/over-ssh-terminal.ts`(Node 24 直接跑 TS) |
28
+
29
+ ```yaml
30
+ shell:
31
+ path: node
32
+ name: SSH / Local
33
+ args: ['<上面那个路径>', '--name', 'gpu01']
34
+ ```
35
+
36
+ 不配也能用,只是 Web 终端永远开在本地(远程文件、远端执行、`@` 补全不受影响)。
37
+
38
+ ## 发布说明
39
+
40
+ 包里带了两个平台的 Tier 2 代理二进制(纯静态,远端零依赖)。源码在配套的 Go 仓库
41
+ `dsh-over-ssh-agent`(暂未公开发布,按下面的 `sourceCommit` 可核对)。这一段由 `npm run release:note` 从
42
+ `agents/agent-manifest.json` 生成,`prepack` 会核对它与清单一致,**不要手改**:
43
+
44
+ <!-- release-note:begin(由 tools/release-note.mjs 生成,勿手改) -->
45
+
46
+ | 平台 | 字节 | sha256 |
47
+ |---|---|---|
48
+ | linux-amd64 | 4,907,168 | `08998e6f51bc1a3759a966e96263c380f81f4281f2a50b0257b61f12b1341c3b` |
49
+ | linux-arm64 | 4,653,216 | `0e19b3e77590f233093c09a7adc20487d721407bf805a9f879a756c9f03b9a11` |
50
+
51
+ - 代理版本:`1.7.0-0ae8c02`
52
+ - 源码提交:`0ae8c02`(Go 仓 `dsh-over-ssh-agent` 的这个提交)
53
+ - 清单原文:包内 `agents/agent-manifest.json`
54
+ - 核对:`sha256sum agents/<平台>/dsh-over-ssh-agent` 与上表比对;按上面的提交检出 Go 仓后
55
+ `scripts/check.sh` 可重建同样的产物。
56
+
57
+ <!-- release-note:end -->
58
+
59
+ ## 关键设计取舍(摘要)
60
+
61
+ - **传输层自研**,不使用核心 `packages/ssh/*` 家族。原因见设计文档 §3.1:官方实现的 `BatchMode=yes` 让密码认证不可能、远端必须预装 Node + helper、`ctx.ssh` 单例、且其 Web 集成需单独实现(preset 的 `isolate` 子树覆盖不到 `agent.ctx`)。
62
+ - **文件按路径路由,进程不路由**:核心 provider 的公开方法收不到 agent/session 身份,因此 fs 用自研路由 provider 支持"本地 + 远端多根",而 bash/终端保持单一执行世界,远端命令走显式入口。
63
+ - **远端零安装(Tier 1,地板)**:只依赖远端已有的 POSIX 工具(`sh`/`base64`/`stat`/`find`/可选 `git`/`rg`),连上后做一次能力探测并按能力降级。
64
+ - **可选 Go 代理(Tier 2,增强)**:独立仓库 `../dsh-over-ssh-agent`,纯静态单文件(随包产物的体积与摘要以 `agents/agent-manifest.json` 的 `bytes`/`sha256` 为准,交叉编译矩阵见 `../dsh-over-ssh-agent/doc/05-benchmarks/baseline.md`;零第三方依赖),经 ssh stdio 通信(不监听端口),补齐原生监听/索引/长任务/远端 LSP/Landlock 围栏。决策见 `doc/03-decisions/0005-optional-go-remote-agent.md`;代理不可用时一律回退 Tier 1。
65
+ - **默认尝试部署**(2026-10-13 起,审计 0040):连接的默认传输是 `agent`,首次连接会把代理装到远端 `$HOME/.cache/dsh-over-ssh/dsh-over-ssh-agent`(约 4.7 MiB / 4,907,168 B,以 `agents/agent-manifest.json` 为准,`0700`),失败自动回退 Tier 1。想避开远端写入就显式写 `transport: alias`。
66
+ - **密码认证同样可用**:代理经系统 ssh 启动,而系统 ssh 靠 `SSH_ASKPASS` 助手取密码(秘密只经环境变量传递,不落盘)。此前"密码认证无法用代理"的说法是把本插件硬写 `BatchMode=yes` 误当成了 SSH 的限制。
67
+ - 随包只带 `linux-amd64` 与 `linux-arm64` 两个产物;其它平台的远端会正常降级到 Tier 1。
68
+ - **秘密进 `ctx.credentials`**:`role('secret')` 只保证不上 wire,落盘仍是 profile patch 明文,因此密码/口令绝不进 Config。
69
+
70
+ ## 目录结构
71
+
72
+ ```
73
+ src/
74
+ host/
75
+ domain/ # 纯 TS、零框架依赖、100% 单测覆盖
76
+ app/ # 应用层用例(依赖抽象)
77
+ infra/ # 传输/后端/缓存/持久化实现
78
+ integration/ # 与核心对接:fs provider、fileReferences、tools、Remote API、终端 wrapper
79
+ client/ # web client 半(React + TS + CSS Modules)
80
+ tests/ # 每个模块一个独立测试文件
81
+ ```
82
+
83
+ ## 测试
84
+
85
+ 零依赖:使用 Node 内置 test runner + 原生 TypeScript 类型剥离(Node ≥ 22.19 / ≥ 24)。
86
+
87
+ ```sh
88
+ npm test # node --test "tests/**/*.test.ts"
89
+ npm run typecheck # 需要先安装 devDependencies(typescript)
90
+ ```
91
+
92
+ 约定:**一个源文件对应一个 `tests/<同名>.test.ts`**;领域层与基础设施层不依赖核心包,因此测试无需安装 `@deepseek-ai/*`。
93
+
94
+ 当前数字只在 `doc/05-benchmarks/baseline.md` 维护,本文不重复抄录。抄录的副本会与基准不一致,这类不一致在本仓库已经出现过。要现在的数字就跑命令:
95
+
96
+ ```sh
97
+ node --test --test-concurrency=4 "tests/**/*.test.ts" # 正确性:用例数与失败数
98
+ tsc -b && tsc -p tsconfig.client.json # 类型:宿主半与客户端半
99
+ npm run build:host && node tools/build-client.mjs --check # 产物体积
100
+ npm run agents # 随包代理清单与体积
101
+ ```
102
+
103
+ > 注意:本机跑套件必须限并发。`npm test` 的默认并发会一次拉起 71 个 Node 进程,而本机可用物理内存常年
104
+ > 只有 1–2 GB,会大量假失败(`spawn ENOMEM` / `Fatal process out of memory`,用例总数在 841–1235 之间跳)。
105
+ > 用 `node --test --test-concurrency=4 "tests/**/*.test.ts"`。这是环境容量问题,不是代码缺陷。
106
+
107
+ ## 里程碑
108
+
109
+ 见设计文档 §13:M0 spike(三个未验证假设)→ M1 传输与连接管理 → M2 路由 fs → M3 远端搜索与 `@` → M4 文件树与传输 → M5 远程终端 → M6 会话管理 → M7 性能与内存回归。
110
+
111
+ 当前进度(本仓库内):只列最近三条,更早的里程碑见 `doc/06-audits/README.md` 的历史记录表。
112
+
113
+ - [x] **改名 `dsh-over-ssh`**:仓库目录、Go module、二进制、状态目录、两个回收站、HTTP 路由共 11 处名字统一,
114
+ 已有数据走 4 条一次性迁移(远端旧代理目录不删)。见 `0110`(`doc/06-audits/0110-rename-to-dsh-over-ssh.md`)。
115
+ - [x] 阶段 2 的「添加目录」按**会话**隔离。同一工作区开两个会话时,A 加的目录 B 既看不到也用不了;fork 出来的会话继承父会话的目录,老数据(无 `sessions` 字段)保持工作区级不打断。会话身份由 `AsyncLocalStorage` 从工具派发带到 `ctx.fs`(平台只给 fs 传 `cwd`)。GUI 文件面板、`glob`/`grep` 与隐式会话根三处残余面已写明。见 `0108`(`doc/06-audits/0108-per-session-root-isolation.md`)。
116
+ - [ ] 阶段 2 的「一键接管」开关尚不可用。审计 0095 的实现有宿主异步清单、行标识与回滚缺陷,不安全的逐行写入路径已移除,面板只显示状态与手动配置提示。平台的单行管理 API 不能保证四行批量原子切换或精确恢复原有 profile 覆盖,要等可靠的批量事务与还原机制可用并通过真实组合验证后才能启用。见 `0096`(`doc/06-audits/0096-takeover-safety-correction.md`)。
117
+ - [x] 阶段 2 的会话工作目录根。活会话 cwd 自动成为会话作用域、隐藏、纯内存的本地根;`resolve`/`lstat` 失败时同步一次再重试。真机端到端未跑。见 `0094`(`doc/06-audits/0094-session-workspace-root.md`)。
118
+ - [x] 卸载不等于关闭。用时间区分重注册与真关闭:`surfaceDetached()` 推迟一个任务、`surfaceAttached()` 取消,调度可注入,策略离线可测。见 `0091`(`doc/06-audits/0091-unmount-is-not-close.md`)。
119
+
120
+ 并行轨道(独立仓库 `../dsh-over-ssh-agent`):协议、文件原语、索引与搜索、watch、proc、LSP、Landlock 围栏均已实现,多会话与常驻 daemon 分四阶段完成。当前状态、未做项与实测数字见该仓库的 `README.md`(`../dsh-over-ssh-agent/README.md`) 与 `benchmark`(`../dsh-over-ssh-agent/doc/05-benchmarks/baseline.md`)。
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": 1,
3
+ "generatedAt": "2026-11-02T16:02:32.695Z",
4
+ "agentVersion": "1.7.0-0ae8c02",
5
+ "sourceCommit": "0ae8c02",
6
+ "artifacts": [
7
+ {
8
+ "platform": "linux-amd64",
9
+ "path": "linux-amd64/dsh-over-ssh-agent",
10
+ "sha256": "08998e6f51bc1a3759a966e96263c380f81f4281f2a50b0257b61f12b1341c3b",
11
+ "bytes": 4907168
12
+ },
13
+ {
14
+ "platform": "linux-arm64",
15
+ "path": "linux-arm64/dsh-over-ssh-agent",
16
+ "sha256": "0e19b3e77590f233093c09a7adc20487d721407bf805a9f879a756c9f03b9a11",
17
+ "bytes": 4653216
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,84 @@
1
+ # dsh-over-ssh 的组合层(profile bundle patch)。
2
+ #
3
+ # 语义要点(0.2.1-rc.2 已核实):
4
+ # - 一条 patch 按 id 定位条目,**整体替换 config**(不做深合并);`name` 只是断言,不能改包。
5
+ # - 被更高层 patch 覆盖时,最后一次写入生效。
6
+ # - `!!js` 只允许出现在 `config` 与 `disabled` 两个字段。
7
+ # - 本文件随包发布(`package.json` 的 `dsh.bundle.patch`),安装即生效。
8
+ #
9
+ # 组合结构:**下沉 + 路由**。
10
+ #
11
+ # 根作用域:
12
+ # ctx.fs ← dsh-over-ssh/fs(多根路由)
13
+ # ctx.fileReferences ← dsh-over-ssh/file-references(多根 @ 补全)
14
+ #
15
+ # 私有域(isolate: fs / fileReferences):
16
+ # ctx.fs ← @deepseek-ai/dsh-fs-sandbox(**原版**,继续服务本地路径)
17
+ # ctx.fileReferences ← @deepseek-ai/dsh-file-reference-local(**原版**)
18
+ # 两个 bridge 把域内实例以 localFs / localFileReferences 暴露给根作用域。
19
+ #
20
+ # 为什么是「接管」而不是「共存」:`ctx.fs` 是 Cordis 单例服务,同一隔离作用域只能有一个提供方,
21
+ # 再注册第二个会抛 `service "fs" has been registered`。`isolate` 让原版实现能在私有域里继续跑,
22
+ # 于是本地路径的**写入围栏由平台的沙箱执行**,本插件不再维护第二份(见 PlatformLocalBackend)。
23
+ #
24
+ # 已实测的前提(cordis 0.2.1-rc.2):域内 `fs` 解析到域内实例;根 `fs` 不受影响;可以同时存在多个域;
25
+ # 根作用域的消费者能注入到域内 bridge 暴露出来的 `localFs`。任何一条不成立这套组合都不成立。
26
+ #
27
+ # 这两组行必须**同时**生效或同时不生效:只禁用核心行而不启用本插件的行,会让 `ctx.fs` /
28
+ # `ctx.fileReferences` 无人提供,所有文件能力一起失效。
29
+ #
30
+ # 还原:禁用整个 bundle。不要只删其中一行。
31
+ #
32
+ # ## 为什么 id 是中文
33
+ #
34
+ # 平台的「插件列表」页**按条目 id 展示**,标题来自各包自己的 `locale/*.json`——而本插件的 7 个条目同属
35
+ # 一个包,共用一个标题,所以**只有 id 能区分它们**(2026-11-01 实测:`locale` 是按包不是按条目)。
36
+ # 之前叫 `remote-workspace-local-fs` 这类名字,用户在列表里看到 7 行猜不出谁是谁。
37
+ # 改成中文是**纯展示层**改动:id 只被 loader 当作键、被 patch 用来定位,实测中文 id 能正常加载
38
+ # (`E2E=1 node --test tests/e2e/host-boot.test.ts` 通过,且启动日志里没有 `localFs` 缺失告警)。
39
+ #
40
+ # 改名的代价(已知并接受):写自己 profile patch 的人要照抄中文 id;`doc/06-audits/` 里的**历史审计**
41
+ # 仍用旧 id(按"历史审计只追加、不改写"的规定保留原样),换算见 `doc/06-audits/log.md` 2026-11-01 那条。
42
+ #
43
+ # 命名约定:`远程工作区-<角色>`。
44
+ # 平台沙箱副本 / 平台文件引用副本 = 平台原版,在私有域里再实例化一次
45
+ # 桥接fs / 桥接文件引用 = 把私有域里的实例暴露回根作用域
46
+ # 路由fs / 路由文件引用 = 根作用域的提供方,决定一个路径走本地还是远端
47
+
48
+ - insert:
49
+ # 主入口:连接 / 根 / 传输 / 索引 / 工具面 / 客户端数据通道 / 终端清单。
50
+ - id: 远程工作区
51
+ name: dsh-over-ssh
52
+
53
+ # 私有域:把**原版** provider 原封不动地再实例化一遍,隔离在这个 group 的槽位里。
54
+ # 原版一行代码没改;`isolate` 让它们注册进本域,不与根冲突。
55
+ - id: 远程工作区-私有域
56
+ name: cordis:group
57
+ group: true
58
+ isolate:
59
+ fs: true
60
+ fileReferences: true
61
+ config:
62
+ - id: 远程工作区-平台沙箱副本
63
+ name: '@deepseek-ai/dsh-fs-sandbox'
64
+ - id: 远程工作区-平台文件引用副本
65
+ name: '@deepseek-ai/dsh-file-reference-local'
66
+ # 两个 bridge 分开:file-reference-local 依赖 workingDirectory(在下游),不能和 fs 挤在同一行。
67
+ - id: 远程工作区-桥接fs
68
+ name: dsh-over-ssh/local-bridge
69
+ inject: [fs]
70
+ - id: 远程工作区-桥接文件引用
71
+ name: dsh-over-ssh/local-bridge-refs
72
+ inject: [fileReferences]
73
+
74
+ # 根作用域的提供方。两个入口都是「路由型」:本地路径与远端路径由同一套表分发。
75
+ - id: 远程工作区-路由fs
76
+ name: dsh-over-ssh/fs
77
+ - id: 远程工作区-路由文件引用
78
+ name: dsh-over-ssh/file-references
79
+
80
+ # 核心行让位给上面的两个提供方。
81
+ - id: fs-sandbox
82
+ disabled: true
83
+ - id: file-reference-local
84
+ disabled: true