为 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
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 14 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
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。
预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
ahian-lee/dsh-ssh-workspace
为 DeepSeek Harness Desktop 0.2 打造透明 SSH 工作区:本地运行时、远程文件、搜索和 Bash。
相关插件 · UI / 主题
破甲增强版JeffKeeping/dsh-purge-plus
dsh-purge 的非官方分支fork:适配 DeepSeek Harness 0.2.1-rc.2、破甲内核…
★ 387已汉化DeepSeek 客户端
71.9
无代码业务系统平台nocobase/nocobase
NocoBase 是什么
★ 24,804已汉化DSH
71.8
1Panel 服务器面板1Panel-dev/1Panel
全球 250 万+ 自托管用户共同喜爱。
★ 37,344已汉化DSH
71.5
终端编码代理esengine/DeepSeek-Reasonix
· 指南 · ACP · 扩展开发 · 规格 · 官方网站 · Discord · QQ
★ 36,045已汉化DSH
71.2
工作状态行动态ccch1mneyyy/working-activity
作者:chimney@ccch1mneyyy。社区项目,非 DeepSeek 官方项目。
★ 664已汉化DSH
71.2
多模型配置管家farion1231/cc-switch
跨平台桌面一体化助手,支持 Claude Code、Codex、OpenCode、DeepSeek Harness 等工具,唯一官网 ccswitch.io
★ 151,202已汉化DSH
71.1
这是你的仓库?认领条目