dsh-rfiles

remotenpmMIT

Adds chunked uploads with progress and resume, and native downloads of workspace files and the session log ZIP, to the DSH Web GUI, including sessions reached through a paired remote link.

下载量 / Downloads
423

Install

dsh plugin --profile web add dsh-rfiles

Sandbox test

This plugin has not been tested yet. Submit a test →

README

dsh-rfiles:DSH 远程文件传输伴生插件

一句话:不改第三方远程插件、不改 DSH 核心,给手机 / 远程电脑上的 DSH Web GUI 补上「把文件发给 Agent」和「把工作区文件下载到本机」。
形态:DSH bundle 插件(Host 半区 + 注入的 vanilla JS 小部件),无构建步骤、无运行时依赖。
接口契约见 [CONTRACT.md](./CONTRACT.md);英文文档见 [README.md](./README.md)。

1. 问题背景:远程页上的文件传输为什么是坏的

用户通过第三方插件 @linxin666/dsh-remote-web-ui 的配对页(/pair-app)从手机或另一台电脑访问

DSH Web GUI。该插件用一段解析期 boot 脚本把页面里的「程序化请求」改写到 /remote 前缀:

被改写的载体 · 是否改写 · 说明

fetch · ✅ · /api/、/sidebar/、/git/、/pet/ → /remote/...,并附带设备凭据

WebSocket / EventSource · ✅ · 精确白名单

img / script / iframe 的 src · ✅ · 子资源

a[download] 锚点导航、window.open、location 跳转、form 提交、XHR · ❌ · 下载正是这一类

由此产生三个用户可见的缺陷:

1. 官方上传失败 / 没有进度:官方上传优先使用 Web Worker 里的 XHR(在通道外),只有靠官方预留的

__DSH_FILE_UPLOAD__ 钩子才勉强接回通道;而钩子载体不接收 onProgress,所以大文件在手机上就是黑盒等待。

2. 没有下载入口:手机适配层直接隐藏了 Session 头工具区(原注释:no space on phones);电脑远程虽然看得到

「下载 Session 日志」,但它是一次 <a download> 导航,URL 没有 /remote 前缀,被 DSH 的 /api 围栏挡掉,

返回 401/403 纯文本——UI 却在点击后立刻提示「下载已开始」。

3. Range 被代理丢弃:代理的请求头白名单只放行 host/sec-fetch-site/content-type/content-length/accept/cookie,

响应头只放行 content-type/content-length/content-disposition/cache-control/etag/last-modified;

Range、If-Range、Content-Range、Accept-Ranges 全部丢弃。所以远程页做不到断点续传、视频拖动。

本插件的思路(不碰上游、不碰核心):

  • 上传:自己用 fetch + File.slice 分块 PUT(每块 8 MiB),进度自己算、按 offset 续传,绕开官方 Worker 载体;

上传完成后由宿主侧调用官方 fileUploads 收据 + sessionController.prompt,把文件作为真实附件发给 Agent。

  • 下载:自建 /api/rfiles.download exact 路由(Content-Length / Content-Disposition / 单段 Range),

客户端用原生 <a download>;远程页给 href 显式加 /remote 前缀。

这一步是关键:锚点不被改写是问题,也恰好是解法——只要 URL 自带 /remote,配对 cookie 就足以通过

远程门,内层再由插件用进程凭据回环取字节,Content-Disposition 被代理透传,浏览器就会正常落盘。

  • 会话日志 ZIP:/api/rfiles.export 转发官方 /api/session.export(includeDescendants=true),并补上下载头。
  • 鉴权:完全不新增。路由注册在 Connection 的 exact Fetch route 上,桌面直接走 owner 鉴权;远程必须

先过 /remote 的配对门,再由代理用进程自己的 browser-auth cookie 回环请求 /api。


2. 能力一览

能力 · 说明

发送文件给 Agent · 多选任意类型文件;分块上传带进度;完成后可附一段文字,点「发送给 Agent」

拍照 / 相册 · 「拍照 / 相册」按钮(input accept=image/* capture=environment),图片 ≤ 20 MiB 走官方图片附件

只存入工作区 · 切换「只存入工作区」,文件落到 <workspace>/.uploads/(同名自动改名为 name (n).ext),不发给 Agent

下载工作区文件 · 面包屑 + 目录列表(每层最多 500 项),点文件用浏览器原生下载;⤓ 按钮为 fetch+blob 兜底

下载会话日志 ZIP · 下载 tab 顶部「下载会话日志 ZIP」,含子会话与附件对象

进度 / 取消 / 续传 · 按块显示已传/总量;可取消;断网或刷新后 24 小时内重新选择同一文件可续传(localStorage 记录 uploadId+offset)

会话切换 · 面板顶部下拉选择会话(只列活跃会话,默认选最新);旁边「远程」徽标表示当前走 /remote 通道

桌面与远程通用 · 桌面直连时下载走 /api(原生 Range 206 可用);远程自动加 /remote 前缀

大小与配额(默认,均可在配置里调):

  • 单文件 ≤ 2 GiB,暂存区总量 ≤ 10 GiB,单块 8 MiB;超限返回 413。
  • 上传暂存目录:$DSH_HOME/rfile-transfers/<uploadId>/{meta.json,data.bin}(进程重启后可续传,不被 DSH 清理)。
  • 会话日志 ZIP 走官方导出,可能较大;远程代理会以流式转发。

3. 文件与接口

包布局:

仓库根就是 npm 包根:

dsh-rfiles/
  package.json          # dsh.bundle.patch 指向 cordis.patch.yml;main: lib/index.js
  cordis.patch.yml      # bundle 层:insert id: rfiles 挂载行
  lib/index.js          # Cordis 插件:name/inject/apply;注册路由 + 注入客户端脚本
  lib/core.js           # 纯逻辑:路径约束、Range 解析、下载头、分块存储(可单测)
  lib/client.js         # 浏览器小部件(classic script,IIFE,无构建、无依赖)
  test/core.test.mjs    # node:test 单测
  test/routes.test.mjs  # 路由级单测
  scripts/mock-server.mjs  # 契约 mock(客户端 UI 联调)
  scripts/mock.html        # mock 页面(挂载 client.js)
  scripts/install.sh       # 源码目录安装脚本(profile-local,开发用)
  scripts/verify.sh        # 连通性验证脚本
  CONTRACT.md              # 接口契约(只读,不得擅改;不在 npm tarball 内)
  README.md / README.zh.md # 英文 / 中文文档
  LICENSE, assets/icon.png # MIT 许可与图标

Host 路由(全部是 /api/rfiles. 的 Connection exact Fetch route;远程页等价路径为 /remote/api/rfiles.):

路由 · 方法 · 用途

/api/rfiles.health · GET · 诊断:{ok, version, workspaceRoot}

/api/rfiles.config?sessionId= · GET · 配额:{ok, workspaceRoot, limits:{chunkBytes,maxFileBytes}}

/api/rfiles.sessions · GET · 客户端会话下拉:只列活跃会话(id/cwd/title)

/api/rfiles.list?sessionId=&path= · GET · 列目录(不递归、dir 优先、≤500 项)

/api/rfiles.upload.init · POST · {sessionId, mode:"workspace" 或 "send", name, size} → uploadId/offset

/api/rfiles.upload.chunk?uploadId=&offset= · PUT · 原始字节;offset 必须等于已收字节,否则 409 {received}

/api/rfiles.upload.finish · POST · {uploadId};workspace 模式原子移动到 .uploads/,send 模式保留暂存

/api/rfiles.upload.cancel · POST · 删除暂存

/api/rfiles.send · POST · {sessionId, uploadIds, text?} → 官方收据附件 + 触发 prompt

/api/rfiles.download?sessionId=&path= · GET · 文件字节;Content-Disposition + Accept-Ranges;单段 Range → 206/416

/api/rfiles.export?sessionId= · GET · 转发官方会话日志 ZIP

可选配置(写在 profile 的 cordis.patch.yml 里,按 id 覆盖 bundle 插入的挂载行):

- id: rfiles
  config:
    chunkBytes: 8388608          # 分块大小(默认 8 MiB)
    maxFileBytes: 2147483648     # 单文件上限(默认 2 GiB)
    stagingBytes: 10737418240    # 暂存总量上限(默认 10 GiB)
    uploadDir: .uploads          # workspace 模式默认落点
    transferRoot: ''             # 暂存根(默认 $DSH_HOME/rfile-transfers)

4. 安装

4.1 前置条件

  • 已安装 DSH 桌面端,并至少启动过一次(存在 $DSH_HOME/profiles/desktop;DSH_HOME 默认 ~/.dsh)。
  • 远程访问场景需要 @linxin666/dsh-remote-web-ui 正常工作、设备已配对(本插件不依赖它,桌面直连也能用)。
  • Node 由 DSH 自带,不需要额外安装依赖(插件只用 Node 内置模块)。

本包是 DSH bundle:package.json 声明 dsh.bundle.patch 并附带 cordis.patch.yml,

安装包即注册 profile 层(insert 一行 id: rfiles)。下面三种方式任选其一。

4.2 从 DSH Web GUI 安装(推荐)

1. 打开 设置 → 插件(Plugins 页面);

2. 用仓库地址、Release tarball 地址或源码目录的本地路径安装;发布到 npm 后也可直接安装

dsh-rfiles。确认新 bundle 处于启用状态(安装 bundle 默认启用);

3. 若页面提示需要重启,完全退出并重新打开 DSH;

4. 打开任意会话,页面右下角出现「文件」按钮即成功。

远程场景仍需 @linxin666/dsh-remote-web-ui 正常工作且设备已配对;桌面直连无需它。

4.3 用 CLI 安装

dsh plugin --profile web add dsh-rfiles

# 从 git 或本地 tarball(npm pack 产物)安装也可以:
dsh plugin --profile web add git+https://github.com/Aperops/dsh-rfiles.git
dsh plugin --profile web add /path/to/dsh-rfiles-0.1.0.tgz

装完检查两处:

1. profile 的 package.json 的 dsh.profile.bundles 里应有 "dsh-rfiles";若没有(部分

CLI 版本只装依赖、不改 bundle 列表),手动追加后重启 DSH:

   "dsh": {
     "prof

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::2,最后更新 2026-10-30

Links

GitHub · Homepage · npm

Same category