dsh-remote-skills
在本地 DSH 里远程使用服务器上的 skills。
把技能目录、命令执行、文件读写三件事接到远端主机:技能列表可读、可加载, 命令实际在远端执行,而不是把技能目录拷到本机。
安装
桌面端 App
-
在插件目录安装依赖:
cd /path/to/dsh-remote-skills && npm install -
把插件加进 desktop profile。
编辑
~/.dsh/profiles/desktop/package.json,在dependencies里加一行、在dsh.profile.bundles末尾加一项:{ "dependencies": { "dsh-remote-skills": "link:/绝对路径/dsh-remote-skills" }, "dsh": { "profile": { "bundles": [ "@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-remote-skills" ] } } } -
在 profile 目录安装:
cd ~/.dsh/profiles/desktop && pnpm install -
完全退出 App(
⌘Q,不是关窗口)后重新打开。不需要手动改
cordis.patch.yml。插件自带的 bundle patch 只禁用了宿主的fs-sandbox—— 因为插件要提供混合文件系统(远端路径走 SFTP、本机路径走本机), 而 cordis 里同一个服务名只能注册一次。bash没有被接管:本机bash行为与不装插件时完全一致,远端命令走独立的remote_bash工具。
命令行 / Web
dsh plugin --profile web add /绝对路径/dsh-remote-skills
同样需要在 ~/.dsh/profiles/web/cordis.patch.yml 里加上面那三行 disabled,然后重启。
拷到另一台机器
插件目录是自包含的(零 npm 依赖,连接层用系统 ssh)。整个目录拷走即可。
配置界面
安装并重启后,打开 插件 → 远程技能,就是配置页面。
远程技能
通过 SSH 使用服务器上的技能。密码只写不回显,保存后即时生效。
[ 测试连接 ] 检查 SSH 连接、远端 Python 与技能发现
连接
主机地址 服务器地址
端口 默认 22
用户名 SSH 用户名
密码 只写不回显;留空表示不改
技能凭据(必填)
数据平台账号 * 注入为环境变量 JOJO_USERNAME
数据平台密码 * 注入为环境变量 JOJO_PASSWORD;只写不回显
远端环境
远端系统 windows / linux / darwin
远端工作目录 命令执行的默认目录
远端文件系统根 文件读写的根目录
Git Bash 路径 仅 Windows 需要
技能发现
远端技能根目录 每行一个路径
只加载这些技能 白名单,每行一个技能名;留空 = 全部
技能优先级 数字小的优先
────────────────────────────────
[ 保存(N 项改动) ]
带 * 的是必填项。没填完时保存按钮旁会提示还差哪几项,不会写进配置。
技能脚本里直接读
$JOJO_USERNAME/$JOJO_PASSWORD,所以填在这里就不需要 再去改远端的.env文件。
测试连接
点 「测试连接」 逐项检查,失败能看出是哪一步断的:
✓ 配置完整 必填项齐全
✓ SSH 连接 已连接 user@host:22
✓ 远端 Python 远端解释器路径
✓ 技能发现 2 个:data-warehouse-ops、huawei-dlf
耗时 1571 ms
保存
所有字段共用底部一个保存按钮。改过的字段会标「已修改,未保存」, 按钮上显示改动数量。
- 只有改过的字段会被写入
- 把某个字段清空 = 取消该项覆盖,回落到默认值
远端系统
| 值 | 说明 |
|---|---|
windows | 需要填 Git Bash 路径 |
linux | 用系统 bash |
darwin | 用系统 bash |
给别人安装:一条命令
插件发布在公共 npm 上,别人不需要 token、不需要改 .npmrc:
dsh plugin --profile desktop add @fanshuishui/dsh-remote-skills
或者在「添加插件」对话框里直接输入 @fanshuishui/dsh-remote-skills。
插件零依赖(连接层用系统自带的
ssh命令),所以安装不需要改pnpm-workspace.yaml、不需要allowBuilds、不需要任何 pnpm 配置。
装完打开「插件 → 远程技能」填配置:
| 字段 | 说明 |
|---|---|
| 主机地址 / 用户名 / 密码 | SSH 连接 |
| 数据平台账号 / 密码 | 技能脚本读取 $JOJO_USERNAME / $JOJO_PASSWORD |
| 远端工作目录 / 文件系统根 | 例如 C:/Users/你的用户名 |
保存后 ⌘Q 重开 App。插件页会出现「更新到 x.y.z」,点一下即可升级。
内网私有 registry(可选)
如果要用公司 GitLab 而非公共 npm,在设置页填「插件 registry」和「GitLab Token」,
插件会自动写 .npmrc(只写 @你的scope:registry 与 _authToken,绝不改全局
registry= —— 全局改会让 ssh2 这类公共依赖也去私有源找而装不上)。
⚠️ GitLab 的 npm registry 不支持 UI 一键更新:它返回的包元数据只有
name/version/dependencies/dist等标准字段,dsh字段被丢弃, 于是 DSH 判定「这个版本没有声明组合包,无法安装」。这是 GitLab 的实现限制, 想用 UI 更新按钮必须走公共 npm(或自建 Verdaccio 这类保留自定义字段的 registry)。
卸载插件
不需要脚本,也不需要手动清理配置。
插件自带的 bundle patch 负责禁用它与宿主冲突的 fs-sandbox,卸载后 bundle
patch 自动失效、fs-sandbox 恢复。profile patch 里残留的配置段(host/密码那些)
不会影响启动 —— 实测确认。
⚠️ 一个坑:如果你在 profile 的 cordis.patch.yml 里手写过
- id: fs-sandbox / disabled: true,卸载插件后它不会消失,会导致 fs 服务
彻底没人提供、工作区(会话列表)整片空白。
# ❌ 不要写在 profile patch 里(bundle patch 已经做了)
- id: fs-sandbox
disabled: true
「检测配置」会检查这一项:补丁:fs-sandbox(profile 里),有多余禁用会直接
指出并告诉你怎么删。
更新插件(不用删掉重装)
先说清楚 DSH 的 UI 能做到什么、做不到什么(0.2.2 实测):
| UI 上的东西 | 实际作用 | 对本插件(git 安装) |
|---|---|---|
| 「添加插件」对话框 | 安装新插件 | ❌ 重装同名包会被拒:already-installed |
| 插件页「刷新」 | 只是重新读取插件列表(controller.load()) | ❌ 不检查更新、不安装 |
| 「所有插件均为最新版本」(仅「发现」tab 开启时) | 统计 updatable 的包 | ❌ git 来源 updatable: false,不计入 |
| 「更新」按钮 | 只对 npm registry 来源的包提供 | ❌ 见下方条件 |
「更新」功能的后端判定(updateRefusal)要求:
parsed.kind === "registry" && parsed.name === bundle.name
也就是必须从 npm registry 按自己的包名安装。我们从 git+ssh:// 安装,
不满足这个条件,所以拿不到一键更新。
这也是「添加插件」里那行提示「其他插件请先卸载再安装新版」的来源 —— 不是 HMR 的限制,是安装入口拒绝重复安装同名包。
正确的更新方式:一条命令,不卸载、不丢配置
cd ~/.dsh/profiles/desktop
pnpm add -w "git+ssh://[email protected]/fanshuishui/dsh-remote-skills.git#v0.16.1"
或者直接用仓库里的脚本(会自动装完并触发热重载):
./scripts/update.sh # 更新到 main 最新
./scripts/update.sh v0.16.1 # 更新到指定 tag
配置不会丢:cordis.patch.yml 里你填的主机/账号/密码/白名单完全不动,
只有 node_modules 里的包文件被替换。
不用重启:装完后 HMR 会在下一次 profile 重新组合时换上新代码。脚本最后会
替你触发这一次(往 cordis.patch.yml 追加一行时间戳 —— 必须是真内容变化,
touch 只改 mtime 不生效)。手动做也可以:
echo "# updated $(date)" >> ~/.dsh/profiles/desktop/cordis.patch.yml
源码开发时想改完即生效
默认 HMR 的 root 是 [](不监听源码),改 lib/*.js 不会自动生效。加:
- id: hmr
disabled: false
config:
root: ["/绝对路径/到/dsh-remote-skills"]
root 必须写绝对路径(. 是相对 profile 目录,link: 的源码在别处)。
之后改代码保存,1~2 秒自动重载。
什么时候才真要重启
只有改组合结构才需要:package.json 的 dsh.profile.bundles、
pnpm-workspace.yaml。普通代码更新都不用。
改动后跑测试
npm test # 纯本地,49 项(配置解析/frontmatter/路径/补丁修复/profile/诊断/状态)
npm run test:remote # 需要真实服务器,12 项(SSH/shell/fs/skills/remote_bash/python)
test:remote 从环境变量读连接参数,没配置就整体跳过:
DSH_TEST_HOST=your-server.example.com DSH_TEST_USER=your-ssh-user DSH_TEST_PASSWORD=*** npm run test:remote
用
npm test而不是pnpm test—— pnpm 会在跑脚本前执行依赖检查,
这些用例不是凭空写的,每一组都对应一个真实踩过的问题,见 CHANGELOG。
命令在哪执行
插件不接管 bash。本地与远端各用各的工具,边界由工具名表达:
| 场景 | 用什么 | 在哪执行 |
|---|---|---|
本机命令(ls、git、pnpm、uname…) | bash | 本机,与不装插件时一致 |
| 远端命令(技能脚本、远端数据文件) | remote_bash | 远端主机 |
| 远端文件读写 | read / write / edit | 远端(路径是远端形态时自动走 SFTP) |
| 本机文件读写 | read / write / edit | 本机 |
实际效果:
bash: uname -s → Darwin (本机 Mac)
remote_bash: uname -s → MINGW64_NT-10.0-19045 (远端 Windows)
技能正文会自动注入一段说明,告诉模型本技能在远端、脚本请用 remote_bash。
所以你不需要改技能文档。
早先版本尝试过接管
bash(全量重定向 / 按路径智能路由),都放弃了: 前者把本地开发环境整个拿走,后者两个命令空间不统一(pwd回本机、 紧接着的cd落远端)。独立工具是 MCP 等主流工具的做法。
只加载这些技能
白名单。只有列在这里的技能会从远端加载,留空表示全部加载。
推荐用它而不是「排除某几个」:远端技能目录是活的,白名单下服务器新增的技能 默认不会进来,不会让模型调到一个本机跑不通的技能。