dsh-webdav
Verifieddsh-webdav · v0.4.2 · MIT · Web UI
Universal WebDAV drives for DeepSeek Harness: agent tools (webdav_*), a multi-remote file browser panel and a settings page. Works with 123pan, Jianguoyun, Nextcloud, Synology, fnOS, ZSpace, Alist, Seafile and any RFC 4918 server. · 通用 WebDAV 网盘插件:给 Agent
Install
dsh plugin add dsh-webdav Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-webdav
通用 WebDAV 网盘 的 DeepSeek Harness 插件:把网盘变成 原生 Agent 工具 + 内置技能 + DSH 界面(侧栏多网盘浏览器 + 设置页)。
不是「123云盘专用」——任何遵循 RFC 4918 的 WebDAV 服务端都能用,而且可以同时挂多个网盘,在侧栏下拉切换。
- 零依赖:只用 Node 标准库(
fetch+node:fs/node:stream),不需要 Python,不 import 任何@deepseek-ai/*或第三方包。 - 零构建:host 半边是普通 ESM,client 半边是手写的 lazy-CJS bundle(和官方
dsh-notification/client.js同形),没有编译步骤。 - 凭据不入库:凭据只写本机
~/.config/dsh-webdav/credentials.json,代码可分享、密钥不分享。
npm i -g dsh-webdav # 或装进 DSH profile(见下文「安装」)
支持的网盘
选一个预设、填地址与密码即可;预设还会告诉你去哪儿拿「应用密码」、以及那一家实测踩过的坑。
| 预设 | 地址形态 | 密码用什么 |
|---|---|---|
| 123云盘 | https://webdav.123pan.cn/webdav |
手机号 + 「工具中心 → 第三方挂载」的应用密码(会员权益) |
| 坚果云 | https://dav.jianguoyun.com/dav/ |
注册邮箱 + 「安全选项 → 添加应用密码」 |
| Nextcloud / ownCloud | https://<主机>/remote.php/dav/files/<用户名>/ |
建议用「安全 → 生成应用密码」 |
| 飞牛 fnOS | http://<NAS地址>:5005/ |
控制中心 → 文件服务 → WebDAV(默认 HTTP 5005 / HTTPS 5006) |
| 群晖 Synology DSM | http://<NAS地址>:5005/ |
需先装 WebDAV Server 套件(默认 5005 / 5006) |
| 极空间 ZOS | http://<NAS地址>:5005/ |
系统设置 → 网络服务 → 文件访问 → WebDAV |
| Alist / OpenList | http://<主机>:5244/dav/ |
Alist 登录账号密码(地址必须带 /dav) |
| Seafile | https://<主机>/seafdav |
「设置 → 其他 → 生成 WebDAV 密码」 |
| InfiniCLOUD / TeraCLOUD | https://webdav.teracloud.jp/dav/ |
网页「アプリ連携 → WebDAV」的专用密码 |
| Koofr | https://app.koofr.net/dav/Koofr/ |
Preferences → App passwords |
| Yandex Disk | https://webdav.yandex.com/ |
Yandex ID 的应用专用密码 |
| Mail.ru Cloud | https://webdav.cloud.mail.ru/ |
账号安全设置里的应用密码 |
| OpenDrive | https://webdav.opendrive.com/ |
注册邮箱(WebDAV 需付费方案) |
| Box | https://dav.box.com/dav |
仅企业版提供,需管理员开启 |
| 自定义 / 其它 | 手填完整地址 | Apache mod_dav、其它 NAS、WebDAV 网关都选这项 |
自建 NAS 基本都在局域网里:跨设备取件前先确认 DSH 所在机器能连通那个地址。
功能
1. Agent 工具(11 个,全部支持 remote 参数)
| 工具 | 用途 |
|---|---|
webdav_remotes |
列出已配置的网盘(id / 名称 / 地址 / 打码账号 / 默认),多网盘时先跑这个 |
webdav_check [remote] |
连通性自检:HTTP 状态、DAV 等级、根目录条目数、凭据来源 |
webdav_ls [remote] [路径] |
列目录,默认根 /,目录在前 |
webdav_stat [remote] <路径> |
查类型/大小/时间;不存在时返回 exists=false 而不报错 |
webdav_get [remote] <远端> [本地] |
下载到本机(流式) |
webdav_put [remote] <本地> [远端目录] |
上传(流式;远端目录缺失时自动补建) |
webdav_mkdir [remote] <路径> |
新建目录(只建一级) |
webdav_mv [remote] <源> <目标> |
移动 / 重命名(目标撞名会自动处理覆盖) |
webdav_rm [remote] <路径> [recursive] |
删除;目录必须显式 recursive: true |
webdav_read [remote] <路径> [maxBytes] |
直接读远端小文本文件进上下文(默认 ≤256KB) |
webdav_url [remote] <路径> |
打印 WebDAV 直连地址(不是分享链接) |
remote 是网盘 id,省略就用默认网盘。远端路径以 / 开头或不以 / 开头都行;中文名自动 URL 编码,不要手工编码。
2. 设置页:设置 → WebDAV 网盘
- 网盘列表:每张卡片显示名称 / 预设 / 地址 / 打码账号 / 是否默认 / 是否缺密码, 可测试连接、编辑、设为默认、删除;被环境变量盖住的字段会明确标出
- 添加 / 编辑网盘:选预设自动填地址(并自动选中
<主机>占位符,直接打 IP 就能覆盖), 就地显示该预设的「怎么拿密码」与「已知坑」 - 偏好:请求超时、
webdav_read上限、列目录条数上限、大文件警告阈值 - 保存后立即生效,不需要重启 DSH
3. 侧栏标签页「网盘」
装了 dsh-better-sidebar 时,侧栏会多出一个 「网盘」标签页,点开即完整文件浏览器:
- 多网盘切换:工具栏下拉切换网盘(只配一个时自动隐藏);每个网盘各自记住上次所在目录
- 面包屑导航、进入目录、下载、预览
- 搜索 / 排序 / 筛选:按
/聚焦搜索框即时按名称过滤,「仅文件夹」开关; 可按名称 / 修改时间 / 大小 / 类型排序(点表头或工具栏按钮切换升倒序,目录永远在前,名称按数字序) - 多选与批量操作:行首复选框 + 表头全选,可批量下载 / 移动 / 删除; 批量任务显示进度条,随时可点「停止」;上传失败不再中断整批,最后汇总失败清单
- 重命名:行内编辑(
F2或右键/⋯菜单),只改名字,Enter提交、Esc取消 - 移动到…:可视化目录选择器(逐级点进去选目标,还能就地新建文件夹),不用手打路径; 文件夹移入自身会被自动跳过,移动后可一键「打开目标目录」
- 右键菜单 /
⋯按钮:打开、预览、下载、复制下载链接、复制路径、重命名、移动到…、删除 - 键盘操作:
Ctrl+A全选、F2重命名、Delete删除、↑/↓移动光标行、空格勾选、Enter打开或预览 - 上传(按钮 + 拖拽,带进度)、新建文件夹;删除目录需手输
DELETE确认 - 窄栏适配:侧栏窄的时候自动收起大小 / 修改时间列,名字不被挤没
- Markdown 预览:
.md默认渲染(标题/列表/表格/代码块/引用/行内样式),可切「原文」对照
页面也可以单独访问:http://127.0.0.1:19387/webdav(设置页 ?view=settings)。
4. 同源 JSON API
页面用的接口,也可以自己脚本调(都要带页面里内嵌的 token,请求头 X-Webdav-Token)。
所有内容接口都接受 remote=<id>,省略则用默认网盘。
GET /webdav 文件浏览器页面(token 内嵌)
GET /webdav?view=settings 设置页
GET /webdav/api/config 网盘清单 + 全局偏好 + 预设表(永不含密码)
POST /webdav/api/config {prefs?} / {remote?} / {select?} / {remove?}
GET /webdav/api/status?remote=<id> 凭据来源 + 连通性
POST /webdav/api/test 用临时凭据测连通性,不落盘
GET /webdav/api/list?remote=&path=/ 列目录
GET /webdav/api/stat?remote=&path=/x 单个条目
GET /webdav/api/content?remote=&path=/x 读小文本文件(JSON)
GET /webdav/api/download?remote=&path=/x 下载(浏览器另存为)
PUT /webdav/api/upload?remote=&dir=&name= 请求体即文件字节,流式 PUT 到远端
POST /webdav/api/mkdir {remote?,path}
POST /webdav/api/move {remote?,from,to}
POST /webdav/api/delete {remote?,path,recursive}
服务端差异:这个插件替你消化掉的部分
各家的 WebDAV 实现差别很大。下面这张表是真机实测出来的(123云盘 + 若干知名服务端), 插件用「先乐观尝试 → 失败后自愈 → 记住结论」的方式让同一段代码在谁家都能用:
| 行为 | 123云盘 实测 | 标准/多数服务端 | 插件的处理 |
|---|---|---|---|
PUT 到不存在的父目录 |
自动逐级建目录 | 409 Conflict |
409/404 → 逐级 MKCOL → 重试一次 |
MOVE 到已存在的目标 |
一律 500(Overwrite 头无效) |
正常覆盖 | 先确认源存在 → 删目标 → 重试,并标记 replaced |
MOVE 源不存在 |
500 |
404 |
先查源;源不存在就绝不碰目标(防静默数据丢失) |
MOVE 目标父目录不存在 |
500 |
409 |
二次诊断后提示「目标目录不存在」 |
COPY |
Allow 里声明支持,实测 500 |
可用 | 识别为「不支持」并给出替代方案 |
MKCOL 已存在的目录 |
201(当成功) |
405 |
都当成功,但错误提示按服务端语义走 |
PROPFIND 不带 body |
可用 | 部分 NAS 要求显式 <prop> |
先带显式 body,被拒就自动退回 allprop 并记住 |
getetag |
空 | 有 | 有就用,没有不报错 |
getcontentlength |
目录无 | 目录无 | 拿不到就是 null,不会当成 0 |
resourcetype / displayname |
有 | 多数有 | 两种命名空间前缀(d: / 无前缀)都能解析 |
| 不支持的属性 | 回 404 propstat 里放同名空元素 | — | 只在 2xx propstat 里取值,避免读到空值 |
Allow 头 |
不可信(列了 COPY 却不工作) | — | 能力靠实测,不读 Allow |
| 被反代到登录页 | — | 返回 200 + HTML |
识别为「不是 WebDAV 响应」并报错,绝不静默当成空目录或把登录页存成文件 |
123云盘还有一条内容层面的最终一致性:下载走签名 CDN,刚覆盖写入的文件立刻读回可能仍是旧内容
(实测 PUT 新内容后 GET 仍返回上一版,几秒后正常)。这不是写入失败。
学到的能力缓存在 ~/.config/dsh-webdav/capabilities.json,按「网盘 id + 地址」存;地址一改就自动作废。
安装
方式一:DSH profile(推荐)
在 profile 的 package.json 里加依赖,并把包名加进 dsh.profile.bundles:
{
"dependencies": { "dsh-webdav": "link:D:/agent/dsh-webdav" }, // 或版本号
"dsh": { "profile": { "bundles": [ /* ... */ "dsh-webdav" ] } }
}
包内的 cordis.patch.yml 会插入 id: dsh-webdav 这一行,因此在「设置 → 插件」里可见、可停用。
改
package.json与 host 代码后需要重启 DSH;改lib/ui.html只要刷新页面(按 mtime 缓存)。
方式二:从 npm 安装
npm i -g dsh-webdav
凭据与配置文件
刻意把密码和能进备份的配置分开存,三份文件都在 ~/.config/dsh-webdav/:
| 文件 | 内容 |
|---|---|
config.json |
非机密:网盘清单(id / 名称 / 预设 / 地址 / 默认上传目录)+ 全局偏好 |
credentials.json |
机密:每个网盘的账号与应用密码(0600,Windows 下用 icacls 收紧到仅当前用户) |
capabilities.json |
运行时学到的服务端能力(上面那张表的结论) |
也可以用环境变量临时顶替:
WEBDAV_URL=https://dav.example.com/dav/
[email protected]
WEBDAV_PASSWORD=<应用密码>
WEBDAV_REMOTE=nas # 可选:指定这些变量作用于哪个网盘(省略则作用于默认网盘)
凭据禁止写入记忆、聊天正文、仓库或任何会同步的位置。 多数服务商要求用「应用密码」而不是登录密码。
想换配置目录(便携安装 / 一台机器跑多套配置),在插件 config 里设置 configDir:
- id: dsh-webdav
config: { configDir: 'D:/portable/dsh-webdav' }
自定义目录是完全自包含的:三份文件都在里面,且不会去读 ~/.config/123pan/ 的老配置
(迁移只针对默认位置,免得「换个目录」把真实老凭据意外带进来)。
从 dsh-pan123 升级
老版本(≤0.3.0)的凭据会被自动接管,不需要重新输一次:
~/.config/123pan/webdav.env→ 解析成一个新的网盘条目写进config.json/credentials.json~/.config/123pan/settings.json里的偏好转进config.json- 迁移是幂等的:只要
config.json已存在就不会再动它 - 老的
pan123-*工具名、/pan123路由、pan123-webdav技能不再提供; 内置的pan123-webdav影子技能会把模型指到新的webdav技能,避免它再去跑scripts/pan123.py
老的 ~/.config/123pan/ 目录会保留不动,确认新配置工作正常后可以自行删除。
开发
node --test # 全部测试(不写死数量,免得文档过期)
node --test test/interop.test.js # 只跑「多服务端行为档位」互操作矩阵
npm pack --dry-run # 看发布产物
测试里最值得一提的是两层:
test/fake-webdav.js:可编排的假 WebDAV 服务端,用profile复刻真实服务端行为——standard(RFC 标准)、123pan(自动建目录 / MOVE 撞名 500 / COPY 500 / 404 propstat 空元素)、minimal(必须带显式<prop>/ 不支持 MOVE、COPY)、login-redirect(200 + HTML 登录页); 还能开noPrefix用默认命名空间。真机比假服务器更弱这件事就是靠它对出来的。test/interop.test.js:同一套操作跑遍所有 profile,断言最终结果一致—— 这是「通用」这个说法唯一的证据。
真机验证脚本(不进仓库,需要真实凭据):覆盖连通性、多级目录上传、中文名、撞名 MOVE、 源不存在时的安全护栏、递归删除护栏、能力缓存回写。
边界与已知限制
- 协议本身:WebDAV 没有服务端搜索(界面里的搜索只作用于已加载的当前目录)、没有分享链接、
多数服务端没有回收站(
DELETE就是真删)。 - 目录删除是递归的:按 RFC 4918,对集合的
DELETE就是递归删除,任何服务端都不会再确认一次。 插件强制要求显式recursive: true,但这只是护栏,不是「能撤销」。 - 认证:目前只支持 HTTP Basic(探测到的服务端基本都是它)。Digest / OAuth 未实现。
- 大文件:WebDAV 没有秒传/断点续传,超过阈值会警告;超大文件建议用官方客户端。
- 部分服务端的最终一致性:123云盘删除后约 10 秒内
stat可能仍报「存在」; 下载走 CDN,覆盖写入后立刻读回可能拿到旧内容。这类现象不是写入失败。 - 能力探测是惰性的:插件不会一上来就在你的网盘里建临时文件去试探, 而是在真实操作失败时才学习并记住结论。所以第一次撞名移动会比之后多花一次往返。
License
MIT