🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

SSH 远程工作区 ahian-lee/dsh-ssh-workspace

DeepSeek Harness
ahian-lee14 天前有提交已提供中文文档扫描:含敏感能力

为 DeepSeek Harness Desktop 0.2 打造透明 SSH 工作区:本地运行时、远程文件、搜索和 Bash。

30.7/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/21 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add ahian-lee/dsh-ssh-workspace
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · ui
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 14 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 94 处,展示前 92 条)
  • 发起网络请求
    lib/shell.js:199
    await client.request("createTerminal", {
  • 发起网络请求
    lib/shell.js:237
    await client.request("disposeTerminal", { channel: terminalUri }).catch(() => {});
  • 发起网络请求
    lib/shell.js:285
    if (terminalCreated) await client.request("disposeTerminal", { channel: terminalUri }).catch(() => {});
  • 读取本地文件
    lib/manager.js:89
    ...JSON.parse(await readFile(settingsPath, "utf8"))
  • 写入本地文件
    lib/manager.js:109
    await mkdir(dirname(settingsPath), { recursive: true });
  • 写入本地文件
    lib/manager.js:111
    await writeFile(temporary, JSON.stringify(next, null, 2) + "\n", "utf8");
  • 写入本地文件
    lib/manager.js:112
    await rename(temporary, settingsPath);
  • 写入本地文件
    lib/manager.js:479
    await mkdir(resolve(tmpdir(), "dsh-ssh"), { recursive: true });
  • 写入本地文件
    lib/manager.js:486
    await mkdir(aliasPath, { recursive: true });
  • 执行外部命令来自配置/变量
    lib/manager.js:748
    const child = spawn(transport.executable, [
  • 发起网络请求
    lib/backend-client.js:27
    return globalThis.fetch(target, {
  • 发起网络请求
    lib/backend-connection.js:58
    async fetch(path, init = {}) {
  • 发起网络请求
    lib/backend-connection.js:60
    return (await this.ready(signal)).fetch(path, init);
  • 发起网络请求
    lib/router-subprocess.js:272
    await client.request("createTerminal", {
  • 发起网络请求
    lib/router-subprocess.js:302
    await client.request("disposeTerminal", { channel }).catch(() => {});
  • 发起网络请求
    lib/router-subprocess.js:326
    await this.client.request("disposeTerminal", { channel: this.channel }).catch(() => {});
  • 发起网络请求
    lib/router-subprocess.js:359
    await this.client.request("disposeTerminal", { channel: this.channel }).catch(() => {});
  • 写入本地文件
    lib/tui-backend.js:1101
    await writeFile(target, archive.data, { flag: "wx" });
  • 写入本地文件
    lib/web.js:124
    await mkdir(directory, { recursive: true });
  • 写入本地文件
    lib/web.js:126
    await writeFile(localPath, bytes, { flag: "wx" });
  • 读取本地文件
    lib/web.js:174
    children = (await readdir(root, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort().reverse();
  • 读取本地文件
    lib/web.js:198
    await access(path, process.platform === "win32" ? constants.F_OK : constants.X_OK);
  • 读取本地文件
    lib/web.js:206
    await access(path, constants.F_OK);
  • 执行外部命令来自配置/变量
    lib/web.js:224
    const child = spawn(editor.executable, [...editor.prefixArgs, ...editorLaunchArgs(sshTarget, remotePath)], {
  • 执行外部命令来自配置/变量
    lib/web.js:253
    const child = spawn(editor.executable, [...editor.prefixArgs, ...args], {
  • 执行外部命令固定命令
    lib/web.js:537
    const child = spawn("ssh", [
  • 发起网络请求
    lib/client.js:192
    const response = await fetch(path, {
  • 写入本地文件
    lib/runtime-B-g9011R.js:222
    if (this.mapper !== void 0) mkdirSync(this.mapper.localWorkspace, { recursive: true });
  • 执行外部命令来自配置/变量
    lib/runtime-B-g9011R.js:352
    const child = spawn(this.config.sshExecutable, [
  • 执行外部命令来自配置/变量
    lib/runtime-B-g9011R.js:388
    const tunnel = spawn(this.config.sshExecutable, [
  • 执行外部命令来自配置/变量
    lib/runtime-B-g9011R.js:422
    const child = spawn(command, args, {
  • 读取本地文件
    lib/tunnel-BO4E7VdW.js:30
    for (const candidate of candidates) if (existsSync(resolve(candidate, "package.json")) && existsSync(resolve(candidate, "lib", "index.js"))) return ca
  • 读取本地文件
    lib/tunnel-BO4E7VdW.js:36
    const manifest = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"));
  • 读取本地文件
    lib/tunnel-BO4E7VdW.js:38
    const files = [...ROOT_FILES.filter((path) => existsSync(resolve(root, path))), ...walkFiles(resolve(root, "lib")).map((path) => relative(root, path))
  • 读取本地文件
    lib/tunnel-BO4E7VdW.js:41
    data: readFileSync(resolve(root, ...path.split("/")))
  • 读取本地文件
    lib/tunnel-BO4E7VdW.js:149
    if (!existsSync(root)) return [];
  • 读取本地文件
    lib/tunnel-BO4E7VdW.js:151
    for (const entry of readdirSync(root, { withFileTypes: true })) {
  • 发起网络请求
    lib/tunnel-BO4E7VdW.js:385
    fetch(path, init = {}) {
  • 发起网络请求
    lib/tunnel-BO4E7VdW.js:389
    return globalThis.fetch(`${this.origin}${path}`, {
  • 发起网络请求
    lib/tunnel-BO4E7VdW.js:396
    const response = await this.fetch("/dsh-host/protocol", signal === void 0 ? {} : { signal });
  • 执行外部命令来自配置/变量
    lib/tunnel-BO4E7VdW.js:408
    const ssh = spawn(config.sshExecutable, [
  • 读取本地文件
    lib/config-BN3gCBP-.js:27
    source = await readFile(absolute, "utf8");
  • 写入本地文件
    lib/config-BN3gCBP-.js:203
    await mkdir(dirname(absolute), { recursive: true });
  • 读取本地文件
    lib/config-BN3gCBP-.js:206
    const current = await readFile(absolute);
  • 写入本地文件
    lib/config-BN3gCBP-.js:219
    await appendFile(absolute, lines.join("\n"), "utf8");
  • 发起网络请求
    src/backend/client.ts:69
    return globalThis.fetch(target, { ...init, headers })
  • 发起网络请求
    src/backend/connection.ts:16
    fetch(path: string, init?: RequestInit): Promise<Response>
  • 发起网络请求
    src/backend/connection.ts:96
    async fetch(path: string, init: RequestInit = {}): Promise<Response> {
  • 发起网络请求
    src/backend/connection.ts:99
    return tunnel.fetch(path, init)
  • 读取本地文件
    src/backend/install.ts:46
    if (existsSync(resolve(candidate, 'package.json')) && existsSync(resolve(candidate, 'lib', 'index.js'))) return candidate
  • 读取本地文件
    src/backend/install.ts:54
    const manifest = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8')) as { name?: string; version?: string }
  • 读取本地文件
    src/backend/install.ts:58
    const paths = [...ROOT_FILES.filter(path => existsSync(resolve(root, path))), ...walkFiles(resolve(root, 'lib')).map(path => relative(root, path))]
  • 读取本地文件
    src/backend/install.ts:64
    data: readFileSync(resolve(root, ...path.split('/'))),
  • 读取本地文件
    src/backend/install.ts:168
    if (!existsSync(root)) return []
  • 读取本地文件
    src/backend/install.ts:170
    for (const entry of readdirSync(root, { withFileTypes: true })) {
  • 发起网络请求
    src/backend/tunnel.ts:116
    fetch(path: string, init: RequestInit = {}): Promise<Response> {
  • 发起网络请求
    src/backend/tunnel.ts:120
    return globalThis.fetch(`${this.origin}${path}`, { ...init, headers })
  • 发起网络请求
    src/backend/tunnel.ts:125
    const response = await this.fetch('/dsh-host/protocol', signal === undefined ? {} : { signal })
  • 执行外部命令来自配置/变量
    src/backend/tunnel.ts:138
    const ssh = spawn(config.sshExecutable, [
  • 发起网络请求
    src/client/api.ts:72
    const response = await fetch(path, {
  • 发起网络请求
    src/client/api.ts:93
    const response = await fetch(path, {
  • 执行外部命令固定命令
    src/profiles/web.ts:264
    const child = spawn('ssh', [...sshArgs, '-T', '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=5', sshTarget, command], {
  • 读取本地文件
    src/routing/manager.ts:215
    try { next = RemoteSshManager.Config({ ...initialEntry, ...JSON.parse(await readFile(settingsPath, 'utf8')) }) as ResolvedConfig }
  • 写入本地文件
    src/routing/manager.ts:233
    await mkdir(dirname(settingsPath), { recursive: true })
  • 写入本地文件
    src/routing/manager.ts:235
    await writeFile(temporary, JSON.stringify(next, null, 2) + '\n', 'utf8')
  • 写入本地文件
    src/routing/manager.ts:236
    await rename(temporary, settingsPath)
  • 写入本地文件
    src/routing/manager.ts:639
    await mkdir(resolve(tmpdir(), 'dsh-ssh'), { recursive: true })
  • 写入本地文件
    src/routing/manager.ts:646
    await mkdir(aliasPath, { recursive: true })
  • 执行外部命令来自配置/变量
    src/routing/manager.ts:901
    const child = spawn(transport.executable, [...transport.args, '-O', 'exit', target], {
  • 发起网络请求
    src/routing/subprocess.ts:273
    await client.request('createTerminal', {
  • 发起网络请求
    src/routing/subprocess.ts:296
    await client.request('disposeTerminal', { channel }).catch(() => {})
  • 发起网络请求
    src/routing/subprocess.ts:323
    await this.client.request('disposeTerminal', { channel: this.channel }).catch(() => {})
  • 发起网络请求
    src/routing/subprocess.ts:350
    await this.client.request('disposeTerminal', { channel: this.channel }).catch(() => {})
  • 读取本地文件
    src/ssh/config.ts:51
    source = await readFile(absolute, 'utf8')
  • 写入本地文件
    src/ssh/config.ts:228
    await mkdir(dirname(absolute), { recursive: true })
  • 读取本地文件
    src/ssh/config.ts:231
    const current = await readFile(absolute)
  • 写入本地文件
    src/ssh/config.ts:244
    await appendFile(absolute, lines.join('\n'), 'utf8')
  • 写入本地文件
    src/ssh/open-file.ts:158
    await mkdir(directory, { recursive: true })
  • 写入本地文件
    src/ssh/open-file.ts:160
    await writeFile(localPath, bytes, { flag: 'wx' })
  • 读取本地文件
    src/ssh/open-file.ts:205
    children = (await readdir(root, { withFileTypes: true }))
  • 读取本地文件
    src/ssh/open-file.ts:243
    await access(path, process.platform === 'win32' ? constants.F_OK : constants.X_OK)
  • 读取本地文件
    src/ssh/open-file.ts:252
    await access(path, constants.F_OK)
  • 执行外部命令来自配置/变量
    src/ssh/open-file.ts:273
    const child = spawn(editor.executable, [...editor.prefixArgs, ...editorLaunchArgs(sshTarget, remotePath)], {
  • 执行外部命令来自配置/变量
    src/ssh/open-file.ts:301
    const child = spawn(editor.executable, [...editor.prefixArgs, ...args], {
  • 写入本地文件
    src/transport/runtime.ts:178
    if (this.mapper !== undefined) mkdirSync(this.mapper.localWorkspace, { recursive: true })
  • 执行外部命令来自配置/变量
    src/transport/runtime.ts:335
    const child = spawn(this.config.sshExecutable, [
  • 执行外部命令来自配置/变量
    src/transport/runtime.ts:364
    const tunnel = spawn(this.config.sshExecutable, [
  • 执行外部命令来自配置/变量
    src/transport/runtime.ts:399
    const child = spawn(command, args, { windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] })
  • 发起网络请求
    src/transport/shell.ts:228
    await client.request('createTerminal', {
  • 发起网络请求
    src/transport/shell.ts:264
    await client.request('disposeTerminal', { channel: terminalUri }).catch(() => {})
  • 发起网络请求
    src/transport/shell.ts:324
    if (terminalCreated) await client.request('disposeTerminal', { channel: terminalUri }).catch(() => {})
  • 写入本地文件
    src/tui/remote-channel.ts:580
    await writeFile(target, archive.data, { flag: 'wx' })
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/cordis-plugin-include@deepseek-ai/cordis-plugin-loader@deepseek-ai/dsh-agent@deepseek-ai/dsh-fs@deepseek-ai/dsh-fs-local@deepseek-ai/dsh-fs-sandbox@deepseek-ai/dsh-settings@deepseek-ai/dsh-spill@deepseek-ai/dsh-spill-local@deepseek-ai/dsh-subprocess@deepseek-ai/dsh-subprocess-local
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 远端目录挂载为工作区
  • 文件搜索在远程执行
  • Bash在远程执行

README

由 DeepSeek 最新模型翻译生成
SSH Workspace:DeepSeek Harness 远程工作区 English · 下载安装包 非官方社区插件,由 ahian-lee 维护;基于 Yan-Zero/dsh-remote-ssh 修改,保留 Apache-2.0 许可证和原作者署名。 在 DSH Desktop 中选择 SSH 主机和远端目录,像普通项目一样建立工作区、创建对话,并使用原有的文件、搜索与 Bash 工具。 DSH 主程序、对话运行时、模型请求和插件仍在本地运行。 插件将支持的工作区文件操作、搜索、子进程和 Bash 路由到对应 SSH 主机。安装它不需要换一份 DSH,也不会把所有插件搬到远端。 添加 SSH 工作区 功能 - 读取 OpenSSH 主机配置,支持 Include 和 ProxyJump;在添加工作区弹窗或“设置 → Remote SSH”中添加新主机。 - 本地项目继续使用 DSH 原生目录选择器;远程项目可浏览目录或填写绝对路径。 - 每个远程项目成为普通工作区,使用独立对话与历史记录。 - 原有文件、搜索和 Bash 工具随工作区切换执行位置;远端失败或映射移除时不会回退本地执行。 - 保留 DSH 本地沙箱和默认权限设置。 - 修复主按钮白底白字;路径为空时明确显示禁用状态及填写提示。
展开完整文档(README 剩余 1 节)
SSH Workspace:DeepSeek Harness 远程工作区

English · 下载安装包

非官方社区插件,由 ahian-lee 维护;基于 Yan-Zero/dsh-remote-ssh 修改,保留 Apache-2.0 许可证和原作者署名。

在 DSH Desktop 中选择 SSH 主机和远端目录,像普通项目一样建立工作区、创建对话,并使用原有的文件、搜索与 Bash 工具。

DSH 主程序、对话运行时、模型请求和插件仍在本地运行。 插件将支持的工作区文件操作、搜索、子进程和 Bash 路由到对应 SSH 主机。安装它不需要换一份 DSH,也不会把所有插件搬到远端。

添加 SSH 工作区

功能

- 读取 OpenSSH 主机配置,支持 Include 和 ProxyJump;在添加工作区弹窗或“设置 → Remote SSH”中添加新主机。
- 本地项目继续使用 DSH 原生目录选择器;远程项目可浏览目录或填写绝对路径。
- 每个远程项目成为普通工作区,使用独立对话与历史记录。
- 原有文件、搜索和 Bash 工具随工作区切换执行位置;远端失败或映射移除时不会回退本地执行。
- 保留 DSH 本地沙箱和默认权限设置。
- 修复主按钮白底白字;路径为空时明确显示禁用状态及填写提示。

Remote SSH 设置

截图使用演示主机和目录,安装包不包含任何个人 SSH 配置。

支持范围

本版已验证 Windows DSH Desktop 0.2.0-rc.2 + Linux/POSIX SSH 远端,要求 Node.js 22+。本地执行使用 Windows PowerShell 沙箱提供者。暂不支持 Linux/macOS 本地主机、Windows SSH 远端、遗留 TUI 适配器和整个 DSH 后端迁移到远端的模式。

这是基于上游 0.2.4 的 Desktop 兼容分支,适配了 DSH 0.2 的服务、设置、Shell 执行结果和 UI 插槽接口,并非原作者发布的版本。

安装

在 DSH Desktop → 插件 中安装以下预构建包,然后从托盘完全退出 DSH 并重新打开:

https://github.com/ahian-lee/dsh-ssh-workspace/releases/latest/download/dsh-ssh-workspace.tgz

安装包包含编译后的 lib/、前端模块及 cordis.patch.yml,无需用户在本机编译。仓库也保留编译文件,供 GitHub 安装使用。

Windows 上独立 CLI 的 Web profile 可使用:

dsh plugin --profile web add https://github.com/ahian-lee/dsh-ssh-workspace/releases/latest/download/dsh-ssh-workspace.tgz
dsh web

独立 CLI 不管理 Electron 专用的 desktop profile,桌面版请使用应用中的插件管理入口。

从 dsh-remote-ssh 切换: 先停用或移除旧 bundle,再启用本插件。两者接管相同的执行服务,不能同时启用。主机目录与工作区映射仍保存在 ~/.dsh/remote-ssh/,无需删除原来的配置、别名目录或对话。独立的 dsh-ssh 运维面板可继续使用。

建立第一个工作区

1. 先在终端确认 ssh 主机别名 能连接;首次主机指纹确认、密码和 MFA 在终端完成。
2. 点击“添加工作区”,选择服务器,填写 /home/user/project 这样的远端绝对路径,或点击“浏览远端”。输入框里的占位示例不表示已经填好路径。
3. 添加工作区,在其中创建对话。也可以在“设置 → Remote SSH”管理主机与工作区。
4. 执行任意远端 Bash 命令时,主动把该远端会话设为“完全权限”。受限模式的远端 Shell 请求会明确拒绝,插件不会偷偷放宽权限;本地默认权限不变。

远端命令继承 SSH 账户的权限,工作区目录只决定路由位置,并不是操作系统沙箱。需要隔离时请使用权限合适的 SSH 账户。

远端需要 OpenSSH、bash、base64、mkfifo、rg,以及兼容的 VS Code Agent Host/CLI 或已有的 VS Code Server。缺少软件会报错,不会静默安装。若远端 rg 已存在但不在 PATH 中,可在 ~/.dsh/remote-ssh/config.json 为该服务器配置 remoteRgCommand。

验证与开发

首版通过 35 项针对性测试,覆盖路由、文件系统、现行 Shell handle、配置持久化、SSH 配置、路径映射、AHP 协商、SOCKS 与 bundle 组合。独立安装打包产物后,验证实际 DSH 组合、浏览器新增主机/工作区、空路径禁用状态和四个 Agent 预设。Linux SSH 实测包含文件读取、Bash pwd 和 ripgrep,验证过程未发起模型推理请求。

pnpm install
pnpm run check
pnpm run pack:release

开发建议 Node.js 24、pnpm 11.7.0。test 只执行本版支持的 Desktop 路由测试,保留的上游 backend/TUI 测试不属于支持范围。build 负责转译与前端打包,不代表全项目 TypeScript 类型检查通过。变更与署名见 CHANGELOG.md、DESKTOP-COMPAT.md、NOTICE。
预览分享卡片长什么样 ↓ahian-lee/dsh-ssh-workspace
分享 · DeepSeek Harness Hub
ahian-lee/dsh-ssh-workspace

为 DeepSeek Harness Desktop 0.2 打造透明 SSH 工作区:本地运行时、远程文件、搜索和 Bash。

这是你的仓库?认领条目

回到顶部