dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
VSCode-style remote SSH development for DSH: host manager, helper deploy pipeline, remote files/terminal/git, transparent remote execution.
中文 | English
Host 进程内的 SSH 主机管理 + helper 推送流水线 + VS Code 式远端工作台 (资源管理器 / 搜索 / 源代码管理 / 提交图 / 多标签编辑器 / 悬浮编辑器 / 并排 Diff / 多终端)。 主机配在 DSH 设置里(“远程主机”项);装了 dsh-better-sidebar 时另有 独立“远程”tab,没装则退到原生右侧栏“远程”页(同内容,引导页自助开,per-session 实例)。 编辑器还额外挂到会话视图槽,与“对话 / 轨迹”并列成一个远程编辑器标签, 这样代码区是整屏宽的;右侧栏那份是窄屏备用面。 P2 起原生工具在远端工作区里透明跑远端(见下)。
┌─ 本机 DSH Host ─────────────────────────────────┐
│ 本插件 Host 半 │ Client 半(浏览器) │
│ 主机/密钥/推送/路由 │ 设置两节 + 原生入口接管 │
│ /api/dsh-remote/* │ 工作台(右侧栏 + 会话视图)│
└──────────────────────┴──────────────────────────┘
│ ssh2(文件/进程/终端)+ helper RPC(远端 fs)
▼
┌─ 远端 Linux ─────────────────────────────────┐
│ ~/.dsh-helpers/<ver>/<arch>/dsh-ssh-helper │
│ 用户指定的远端工作目录 │
└──────────────────────────────────────────────┘dsh plugin --profile web add dsh-remote-vs(从 npm 装,推荐;也可以直接跟仓库装:dsh plugin --profile web add github:xiray-xia/dsh-remote-vs。)
要求 DSH 0.2.1-alpha.3 及以上(engines.dsh 门到底;当前实测基线 `0.2.1-rc.2`)。 --profile web 指明装进哪个 profile —— 本插件是 Web 插件, 必须装进 Web GUI 所用的那个(web 是常见命名,以你自己的 profile 名为准)。
这一条命令就够了,不需要放行任何构建脚本:包本身没有 prepare/postinstall,依赖树里也没有任何 带安装脚本的包(ssh2 是打进产物的,不是依赖);走仓库安装时还带着构建好的 lib/。 以前需要「在 profile 的 pnpm-workspace.yaml 里放行构建脚本后重跑」,那是 pnpm 12 对 「带安装脚本的依赖」的硬策略门(ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED / ERR_PNPM_IGNORED_BUILDS), 依赖侧无法豁免,只能从树里消掉。装完重启 dsh web,打开设置 → 远程主机开配。
包内含预编译的 helper(vendor/dsh-remote-helpers/,约 45 MB),所以首次安装下载量偏大 ——它是唯一的 helper 投放渠道(官方 Release 不带 helper 可执行文件),去掉它就没法给新主机部署。
能力 · 说明 资源管理器 · 懒加载目录树 + 展开折叠 + 隐藏文件开关;右键菜单:新建/改名/删除/复制/移动/粘贴/在此文件夹查找/插入 @ 引用;git 状态色与文件夹聚合标记 搜索 · 两档:文件名(远端 find)与内容(远端 grep)。内容档带范围目录、Aa(大小写)/ab_(全字)/.*(正则)、结果按文件分组、命中高亮,点条目跳到该行 源代码管理 · 仓库目录选择器(工作区里的 git 在子目录时用:浏览远端目录、一键跳到工作区内发现的仓库、手输相对/绝对路径,并逐目录提示会落在哪个仓库);变更/暂存/未跟踪/忽略分桶,未跟踪目录行可点击展开,逐文件与逐 hunk 暂存/还原、丢弃(带确认)、提交;分支/远程/stash 面板;同步(fetch/pull/push) 会话改动审阅 · 输入框上方的可收纳面板,按会话列出改了什么(一行一个文件、文件类型图标、右侧 +a −b);点文件在远程编辑器里打开并画整行着色 + 行内字符级差异,右上角 n/N ↑↓ 跳改动点,悬浮改动行可撤销这处改动(远端 git apply -R)。详见下文「会话改动审阅」 并排 Diff · 左右严格等分 + 中间 gutter 逐 hunk 按钮 + 顶部横向滚动条(一行很长也不会破坏对称)+ 行号钉在左侧,只渲染视口内的行(3000 行文件从 9000 个行容器降到约 174 个,滚动范围与整篇渲染完全一致);可切「变更前 / 变更后」,变更后可编辑并保存 提交图 · SVG 分支/合并图 + 提交详情 + 点击提交展开变更文件 + 进文件看 diff 多标签编辑器 · 打开/保存/重载、脏标记、多文件标签并存;图片预览、Markdown/HTML 预览(源码/预览切换) 悬浮编辑器 · 三个入口:文件树 / 搜索结果 / 源代码管理右键「在悬浮编辑器中打开」,以及双击会话视图的「远程编辑器」标签把当前编辑器整体浮出(同一套标签、预览、Diff、查找、改动标记,不是另写一个编辑器)。窗口可拖标题栏移动、拖八个边角缩放、最大化/还原、双击标题栏切换最大化;最小化成一个可拖动的悬浮球(球上有未保存脏点,点一下或按 Enter 恢复,拖动可换位置);Ctrl+滚轮 缩放编辑器字号;位置、大小、字号和球的位置跨刷新记住 语法高亮 · VS Code Dark+ / Light+ 配色;覆盖 java/aidl/smali、c/c++、go、rust、js/ts、json、py、yaml、xml、html/css、md、sh、properties/rc/toml、diff 等;兜底着色器在语法包没命中时自动接管,不会出现整篇无色。诚实说明:kotlin / gradle / smali 用 Java 语法着色(没有专用语法包),效果是关键字/字符串/注释正确、专有语法不细分 查找 · 编辑器内 Ctrl+F(Aa/ab_/.*、3/17 计数、↑↓、F3/Shift+F3、Esc);选中文本按 `Ctrl+F` 直接填入;查找框钉在右上角,滚动不带走 跳转 · 从搜索结果点进文件 → 跳到该行 + 垂直居中 + 落地行高亮(约 2.6 秒后淡出);同文件反复跳都生效 多终端 · 标签式多终端(+ 新建、✕/中键关闭带确认)、可跨主机;刷新页面按 session id 自动重连回原会话,socket 全断开后会话保留 10 分钟(够刷新、够切浏览器标签页接回),超时才关闭远端 shell 工作区 · 绑定列表 + 路由档位下拉 + 新建工作区双模向导(本地 / 远端)
能力 · 说明 主机管理 · 增删改查、连接测试;证书 / 密码认证;配置存 $DSH_HOME/dsh-remote.json(0600),与 dsh-ssh.json 独立 密钥 bootstrap · 密码主机一键生成专用密钥对并下发 authorized_keys,之后走密钥 推送流水线 · 远端 arch/系统探测 → 版本比对 → 一致跳过 / 否则推送;executable 通道推送自编译 helper。失败后不再每次远端文件操作都重试:按 5 秒起步、上限 5 分钟的退避重试,设置页「推送」按钮可强制立即重试 远端文件 · SFTP 浏览 / 读 / 写 / 新建 / 删除 / 改名。边界已加固:0 字节文件不再卡死,每个 SFTP 操作 60s 超时回收通道;单个目录列表上限 20000 条(按"目录在前、其余按名"排序后取前 20000,宿主日志说明——协议里没有"已截断"字段,界面上没有标记) 远端终端 · ssh2 PTY + WebSocket,xterm 渲染(复制粘贴/主题跟随/刷新重连);重新挂接只接管、不改尺寸(刷新页面不会把运行中的 shell 缩成 80×24);输出积压时对远端施加真背压;单帧上限 1 MiB,超过 64 Ki 字符的粘贴在客户端按码点分块发送,不再撑断连接 并发通道 · 每主机同时最多 6 条远端通道(不再撞远端 sshd 的 MaxSessions);第 7 个排队最多等 30 秒,超时明确报 remote host is busy,而不是无限堆连接 超时与进程清理 · exec 跑在独立会话里(setsid --wait),超时先发 TERM、3 秒后升级 KILL,连同子进程一起收掉;远端没有 setsid --wait 时探测一次即知,退回旧行为并在宿主日志里写明"超时可能没停掉远端命令" Git 图谱 · 远端仓库状态 + 分支 + SVG 提交图 + 提交详情(exec + 前端解析,零 Host 增量) 导入 · 从 ~/.ssh/config、从现有 dsh-ssh.json 一键导入(只读一次,不耦合)
能力 · 说明 Agent 工具 · remote_hosts / remote_exec / remote_read / remote_write / remote_ls P2 透明远端执行 · 远端工作区里原生 read/write/edit/bash/glob/grep/jobs 直接跑在远端 P3 远端 @ 引用 · 远端会话 @ 出远端文件候选(路由 fileReferences,scoped 实时/bare 走远端 rg 索引)
dsh_remote_vs/
├── cordis.patch.yml # 组合声明:插件行 + 私有本地 realm + 三路由 + preset-remote
├── src/
│ ├── index.ts # 插件入口:store/pool/worlds/路由/工具/预设切换接线
│ ├── protocol.ts # 前后端 wire 契约(路由表 + 纯类型,两边共用)
│ ├── paths.ts # $DSH_HOME 下四件套寻址(库/密钥/构建缓存/别名目录)
│ ├── store.ts # 主机 + 绑定 + 墓碑持久化(0600 JSON,原子写)
│ ├── pool.ts # ssh2 长连接池(exec/shell/sftp/putFile,TOFU 主机键)
│ ├── sftp.ts # 远端文件引擎(SFTP 窗口读写/目录树操作)
│ ├── keygen.ts # 密码机密钥 bootstrap(生成→下发→翻转)
│ ├── deploy.ts # 推送流水线(探测/比对/传包/双验/manifest)
│ ├── importers.ts # ssh_config / dsh-ssh.json 解析导入
│ ├── routes.ts # /api/dsh-remote/* 路由表 + 终端 WebSocket 升级
│ ├── terminal.ts # PTY 会话注册表(刷新重连/断开清理)
│ ├── tools.ts # 5 个 Agent 工具定义
│ ├── http.ts / loopback.ts # JSON 体读写 + 回环信任围栏
│ ├── git-format.ts # git 命令构造 + 纯前端解析(Host 零增量)
│ ├── git-ops.ts # 远端 git 全量动作:status/diff/分支/远程/stash/提交/hunk 补丁构造 + 解析器
│ ├── fs-ops.ts # 远端文件操作命令构造(复制/移动/删除/stat)
│ ├── search-ops.ts # 远端内容搜索(grep 构造 + NUL 分隔解析 + 命中切分)
│ ├── p2/ # ── P2 透明路由 ──
│ │ ├── worlds.ts # 世界判定(cwd 键/别名/墓碑)+ RPC/FS/进程远端上下文
│ │ ├── helper-rpc.ts # ssh2 exec 通道上的 helper wire 协议(握手/心跳)
│ │ ├── router-fs.ts # 透明 fs 路由(目标→世界映射/路径翻译/策略重写)
│ │ ├── router-subprocess.ts # 透明进程路由(argv0 归约/pwsh 门)
│ │ ├── ssh-proc.ts # ssh2 进程运行时(setsid 组杀/marker/stdin/spill)
│ │ ├── shell-bash.ts # 路由 bash 执行器(本地原样/远端去 DSH_*)
│ │ ├── router-spill.ts # 透明 spill 路由(session 世界键/远端目录)
│ │ ├── router-refs.ts # 透明 @ 引用路由(agent 世界键,P3)
│ │ ├── file-refs.ts # 远端 @ 搜索(fs-ssh 实时列/rg 索引/官方评分移植)
│ │ ├── bridge.ts # 私有 realm 本地件发布(localFs 等早期服务)
│ │ └── bridge-refs.ts # refs 独立桥(local-refs 依赖 workingDirectory,必须分离)
│ └── client/ # ── 浏览器半(React,打包进 lib/client.js)──
│ ├── index.tsx # 双面决策(better 优先/settle 加速/原生兜底)+ 设置两节 + directoryFlow 双槽接管
│ ├── store.tsx # 双面共用的模块级状态(useSyncExternalStore;打开的文件/终端/视图都在这)
│ ├── RemoteRoot.tsx # 右侧栏挂载面:VS Code 式活动栏(资源管理器/搜索/SCM/提交图/编辑器/终端/工作区)
│ ├── RemoteEditorView.tsx # 会话视图挂载面(与 对话/轨迹 并列的「远程编辑器」标签,整屏宽)
│ ├── RemoteExplorer.tsx # 资源管理器:树 + 右键菜单 + 文件操作 + @ 引用 + 状态标记
│ ├── SearchView.tsx # 搜索面板:文件名 / 内容 两档 + Aa/ab_/.* + 分组结果 + 跳转
│ ├── EditorPane.tsx # 编辑器面板:多标签 + 信息栏(构建戳/tok 计数)+ 图片/MD/HTML 预览
│ ├── FloatingEditorWindow.tsx # 悬浮编辑器窗口(标题栏拖动/八向缩放/最大化/最小化悬浮球/字号缩放)
│ ├── float-tab-trigger.ts # 双击「远程编辑器」标签浮出的文档级监听(标签属于宿主,只能按文字识别)
│ ├── FloatingPlaceholder.tsx # 编辑器浮出后,中心视图与侧栏留下的「已悬浮」占位 + 停靠按钮
│ ├── float-editor.ts # 悬浮窗几何/缩放/表面归属规则(纯函数,自检直接驱动)
│ ├── live-editor.ts # 表面切换时的编辑器缓冲区与滚动/光标交接
│ ├── CodeEditor.tsx # CodeMirror 封装:语法包表/主题/查找框/跳转/兜底着色接线
│ ├── scm/ # ── 源代码管理 ──
│ │ ├── ScmPanel.tsx # 变更/暂存/未跟踪 + 逐文件逐 hunk 操作 + 提交框
│ │ ├── DiffView.tsx # 并排 diff(顶部横向滚动条/hunk 按钮/可编辑变更后侧)
│ │ ├── HistoryPanel.tsx # SVG 提交图 + 提交详情 + 展开变更文件
│ │ ├── actions.ts # 远端 git 动作层(exec 通道)
│ │ ├── diff-model.ts # unified diff → 并排行投影(纯函数)
│ │ └── status-map.ts # 状态分桶 + 树标记(纯函数)
│ ├── vsc/ # ── VS Code 皮肤与控件(零新依赖)──
│ │ ├── kit.tsx / vsc.css # 图标/对话框/右键菜单/分段控件 + 全部样式(.rvs-root 作用域)
│ │ ├── editor-palette.ts # Dark+/Light+ 配色 + 固定类名(rvs-ck-*)
│ │ ├── editor-theme.ts # 编辑器容器主题(明暗热切换)
│ │ ├── editor-langs.ts # 扩展名 → CodeMirror 语法包
│ │ ├── fallback-highlight.ts# 语法包没命中时的兜底着色(用 tokenize.ts)
│ │ ├── FindWidget.tsx # Ctrl+F 查找框(固定右上角)
│ │ ├── reveal-line.ts # 跳转 + 落地行高亮
│ │ ├── tokenize.ts / markdown.tsx # 只读面板分词器 / 零依赖 Markdown 渲染
│ ├── NativeExplorer.tsx# 原生页类型定义 + seat 正文(无 sidebar 兜底)
│ ├── composer.ts / use-remote-chat-target.ts / selection-popup.ts # 远端 @引用/选中加对话
│ ├── api.ts # /api/dsh-remote 类型化 fetch 封装
│ ├── HostsView / WorkspacesView / WorkspaceWizard / TerminalView / XTerm / Dialog
│ ├── SettingsHosts.tsx # 设置两节 occupant(主机管理/绑定列表,自取数)
│ ├── DirectoryFlow.tsx # 原生入口 occupant(本地双模/远端向导)
│ ├── RemoteDirectoryPicker.tsx# 远端目录浏览弹窗
│ └── i18n.ts / theme.ts / ui.tsx # 双语词典/主题信号/共享样式
├── vendor/dsh-remote-helpers/ # helper 种子(随包发布,启动铺缓存,见下)
├── docs/ # 本地工作文档(不推送 GitHub)
│ ├── P2-PLAN.md # P2 架构/分期/验收矩阵(含逐项状态)
│ └── MAINTENANCE.md # 本地私有(.gitignore),不入库:DSH 升级影响面 + helper 升级流程
├── locale/ # 插件元信息双语
└── lib/ # 构建产物(gitignored,link 安装后重启即生效)vendor/dsh-remote-helpers/(入库)是 helper 种子:插件启动时自动铺到 $DSH_HOME/dsh-remote-helpers/(缺啥补啥,永不覆盖),新机器装完即能推送。 新版本 helper 编好后把 4 件套(tarball + sha256 + helper.json + 补丁说明) 换进 vendor 并提交即可(详细步骤在本地维护文档 docs/MAINTENANCE.md §4,该文件被 .gitignore,不随仓库分发)。
.spike/ 与 scripts/(都 gitignored,不随仓库分发)是开发期的验证与自检脚本: .spike/ 里是真机验证脚本 —— p2-router.ts 主回归(真远端 + 真 Cordis + 真路由类)+ grep-probe.mjs 等探针(用本地的 tsconfig.scratch.json 构建),以及不需要真机的 model-check.mjs / reveal-check.mjs / fallback-check.mjs / client-load-check.mjs / tool-schema-check.mjs;scripts/ 里是纯 node 的回归闸门(node scripts/check-*.mjs, 其中 verify-lib.mjs 核对构建产物)。两者都见下文"开发"。
两个挂载面共用一份状态(store.tsx):右侧栏「远程」(装了 better-sidebar 走独立 tab, 否则走原生右栏页)与会话视图「远程编辑器」(与 对话/轨迹 并列,代码区整屏宽)。
保持挂载,切回来不重新拉取、不闪。隐藏期间不做远端工作(目录列表、SCM 的 stash/分支/远程查询、 编辑器图片读取都等回到前台再补一次),可见性是对插件根节点实测的(IntersectionObserver), 不靠宿主传参。
tok N 着色 token 数、已保存状态、重载/保存)。信息栏三个标记是给排障用的:构建戳判定"浏览器跑的是不是刚编的包",tok 0 判定语法包是否命中。
编辑器整体浮到页面最上层的窗口里(同一份标签与缓冲区,关闭/停靠后回原表面)。标题栏可拖动、八个边角 可缩放、可最大化/还原;最小化成一个 44px 悬浮球(不占整条标题栏,可拖到任意角落,单击/Enter 恢复, 有未保存改动时球上带黄点),Ctrl+滚轮 调字号;未保存内容在「主界面 ↔ 悬浮窗」以及标签页切换之间 显式交接(磁盘侧基线单独保留,脏标记不会误判成已保存),不会被切换动作吞掉。
Ctrl+F 查找框固定右上角(position: fixed 锚定编辑器矩形,任何祖先滚动都不带走);选中文本按 Ctrl+F 直接填入;从搜索结果跳转会居中 + 落地行高亮。
(活着的 shell 不会被缩成 80×24——真实尺寸由能测到自己的那个标签发 resize 帧);socket 全断开后 会话保留 10 分钟,超时才关闭远端 shell(以前是永不回收,关掉浏览器后远端 shell 一直占着 sshd 会话槽)。
提示手动切(revealEditor());
dir/),点它展开该目录里的文件(只发一条限定路径的git status -uall -- dir/,成本随目录而不随仓库;再点收起);展开态是「展开那一刻」的快照, 折叠再展开即刷新;
拖住远端命令,也不让宿主内存无界增长);
listing on shows 20000 of N entries。
输入框上方那条可收纳的面板(标题「本次改动」)按会话回答"这一轮改了什么"。数据来自插件自己的 会话改动账本(/api/dsh-remote/changes),不是 DSH 的 workspaceChanges —— 后者看不见被路由的会话 (探针实测:58 个远端会话、0 个改动事件)。
+a −b),宽度与输入卡左右对齐;点标题栏折叠/展开,⟳ 强制立即重扫。没有改动时显示「本会话还没有改动文件。」;统计不出来时显示 具体原因(见下面的边界),不会丢一个空列表让人猜。
表示(被删的字符在新文件里不存在,只能用插入的 widget 画),左侧 gutter 一列改动色条。
n/N ↑↓(快捷键 Alt+Shift+Cmd/Ctrl+↓/↑),在改动点之间循环跳转;编辑器里一敲字,标记即作废并提示「位置已过期」。
git apply -R。上下文对不上时远端直接拒绝(文件一个字节都不动);标签页里有未保存内容时先拒绝,绝不会用远端补丁盖掉你的编辑;成功后光标留在原处, 标记自动重读刷新(不会因为"撤销了"就整片标记消失)。
会话内不再被拉回,换会话重新跟随。跟随能力在 store 层,侧栏从未打开过也照样跟随;在改动面板点 文件属于"看结果",不算手动切换。
只在唯一候选(根下一层只有一个仓库)时自动采用,多个候选会明确提示去「源代码管理」设置里指定。
已知边界(如实写):
创建的)靠慢扫描发现,单次预算最长约 3 分钟,重扫间隔按上次耗时自适应(至少 5 秒,大仓库会 自然拉长);点 ⟳ 可强制立即重扫。
(no git repository at the workspace root, and N candidates below it — set the repository directory in Source Control),不显示空列表。
单 Host 混合路由:原生工具按会话 cwd 落在哪个世界自动分发 —— 远端 alias 走 helper RPC(fs)/ ssh2(进程),本地一切不变;两者同 Host、共 GUI。
$DSH_HOME/remote-ssh/aliases/(空壳,只做路由键),自动注册进原生工作区列表,标题 > - id: remote-vs 下 config.routingMode,volatile,免重启):off(默认,直通本地)/ shadow(记日志仍走本地)/ enforce(真路由)
Remote 预设(bash 工具集);enforce 下远端新会话自动切过去(默认预设才切,不践踏显式选择)grep/glob 经路由调远端 rg(两台实测机自带 rg 13;远端无 rg 则查不到)FS_TOO_LARGE(helper 官方上限),用流读/分页;写无上限(46MiB 整包写实测通过)readFrom 返 tail + 路径,全文住文件里,与官方一致)npx tsc -p tsconfig.scratch.json && node .spike/build/.spike/p2-router.js(真远端 + 真 Cordis + 真路由类,61/61;P0 路由与 GUI 需重启 Host 后冒烟)
docs/P2-PLAN.md(§8 验收矩阵逐项状态)官方从未发布 helper 二进制(Release assets 全空),本插件走自编译:
dsh-v0.2.1-alpha.2 tag(构建基线:对齐的是 wire 协议号,不是 Host 版本号 —— 协议号至今仍是 2,故 Host 升到 alpha.4 / alpha.5 都无需重编)pnpm exec tsx scripts/build-exe-for-ssh-helper.ts --target=node24-linux-x64DSH_GLIBC_CEILING环境覆盖(默认仍 2.28),本次以 2.35 构建(见缓存目录 glibc-ceiling.patch)
verify-ssh-helper-artifact.ts --sandbox=required 8 项全绿$DSH_HOME/dsh-remote-helpers/(*.tar.gz + .sha256 + .helper.json含 binarySha256,供 P2 的 helperHash)
docs/MAINTENANCE.md §4(.gitignore,不随仓库分发)/api/dsh-remote/* 路由仅限 loopback + 同源访问。dsh-remote.json(0600),与现有 SSH 插件同一信任模型。docs/P2-PLAN.md §4 备注。pnpm install
pnpm run typecheck
pnpm run build
dsh plugin --profile web add link:<本目录绝对路径>