DeepSeek Harness Hub
← 返回列表

swarm-apps/dsh-swarmdrop

DeepSeek Harnessspec-screened在 GitHub 查看 ↗
未验证

为你的 DeepSeek Harness agent…

尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/8/24 · 已提供中文文档

直接从你的 DeepSeek Harness 智能体将文件发送到你的手机,并引用手机回传的内容——无需账户、无需公网 IP、端到端加密。

综合分
29.5
GitHub 分
29.5
用户评分
★ Stars
3
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add swarm-apps/dsh-swarmdrop
该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/20(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-agent@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-runtime@deepseek-ai/dsh-client-ui-conversation@deepseek-ai/dsh-client-ui-input-trigger@deepseek-ai/dsh-client-ui-primitives@deepseek-ai/dsh-client-ui-settings@deepseek-ai/dsh-client-ui-sidebar@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-client-ui-theme@deepseek-ai/dsh-commands
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

dsh-swarmdrop

为你的 DeepSeek Harness agent 提供一条通往你自己设备的通道。它可以把刚生成的内容直接推送到你的手机,你也可以用 @ 引用手机发回的内容——无需账号、无需公网 IP、端到端加密。

传输层是 SwarmDrop,通过其 CLI 驱动。

状态:开发者预览版。 dsh 本身声明会有破坏性变更,而本插件依附于其扩展接缝。请固定版本。

也要固定 dsh 版本线。 对等依赖范围针对 0.1.0-rc.x;@deepseek-ai/ 包在 npm 上的 latest 标签仍指向较旧的 0.0.1-rc.x 版本线,因此不固定版本的安装会解析到本插件并不针对的包。

你能得到什么

| 在 dsh 中 | 会发生什么 |
|---|---|
| “把报告发到我的手机” | agent 调用 swarmdrop_send_files;对话中出现一行传输记录,并跟踪其直至完成。 |
| /swarmdrop send ./report.pdf phone | 同上,无需模型往返。 |
| 在输入框中输入 @ | 你的收件箱——你的设备发送到本机的所有内容——作为引用候选。 |
| 你的手机发送一个文件 | 对话中出现一行记录,该项变为可引用。 |
| 侧边栏底部的 SwarmDrop 按钮 | 节点状态和网络态势、启动/停止、已配对设备,以及配对新设备——无需离开 dsh 去终端操作。 |
| 设置 → SwarmDrop | 所有需要空间的内容:邀请及其撤销、完整收件箱、传输历史及其控制项、本机名称和接收目录、引导节点。 |

面板

设置旁边的圆点表示节点是否正在运行:绿色表示运行中,琥珀色表示已停止,灰色表示第一个响应仍在途中。打开它会给你

- 节点——运行中或已停止、其节点 ID,以及一个用于切换的按钮。
- 网络——NAT 类型、中继预留、引导、已连接对等节点、监听地址。仅在节点运行时显示,因为否则每个字段都会显示“unknown”,不会提供节点行之外的信息。
- 设备——已配对的内容以及是否在线。unknown 是它自己的状态,不是离线的同义词。
- 配对——“添加设备”会签发一个邀请并值守岗位,然后打开一个对话框,将其显示为二维码供你的手机扫描,链接则放在复制按钮后面,以备无法扫描时使用。当设备出现时,第二个对话框会在你决定之前给出其名称、系统、链接类型和完整节点 ID——无论面板是否显示,它都会打开,因为无人应答的请求只会让设备超时。

配对仍然需要有人查看对端的身份——这一点并未放宽,只是转移了位置。邀请是一次性能力,以链接形式传递,谁先出示谁就消耗它,因此除非有人在岗位值守,SwarmDrop 的节点会拒绝所有入站请求。
只要你没有按下“取消配对”,服务台就会一直有人值守,而不是直到你关闭对话框为止——复制链接正是为了把它粘贴到别处,所以对话框总是在配对进行到一半时被关闭,而关闭它绝不能终止对端已经进行到一半的事情。Pairing 区域会保留一行“配对进行中”,并提供一个返回入口,同时侧边栏圆点会变成琥珀灰色,这样即使不打开任何东西,也能看到有服务台仍在值守。

二维码由 swarmdrop 本身渲染,而不是由浏览器渲染。SwarmDrop 通过一个模块对其所有界面进行编码,而该模块还决定二维码上能容纳多少个可拨号地址,并丢弃其余地址——这项工作需要展开邀请的结构。浏览器中的第二个编码器会悄悄生成摄像头无法读取的二维码。

设置页面

面板是一盏状态灯;页面才是偶尔进行工作的地方。七个部分,每个部分在你打开时读取一次,并在你要求时重新读取——绝不按定时器读取,因为它们每一个都要消耗一个 swarmdrop 进程。

- Overview — 面板的事实,未经删减:每个监听地址、完整的节点 ID、每台已配对设备及其身份。
- Invites — 这台机器已发出的邀请,以及一个将其收回的按钮。邀请有效期为 24 小时,重启后仍然有效,并且持有它的人可以配对,所以这是阻止已泄露邀请的唯一方法。
- Inbox — 所有已到达的内容、它们落在哪里,以及将副本导出到别处。
- Transfers — 历史记录,仅在 CLI 会接受的地方提供暂停 / 继续 / 取消。
- Settings — 这台机器的设备名称和接收目录。每个值都会说明它来自哪里,当环境变量优先时,它会说明什么被压制了——否则你会编辑一个不起作用的字段。
- Bootstrap — 这台机器使用的中继和引导节点、它们的连接状态,以及当某个节点无法启动时内核自己的说法。你的添加和删除会叠加在内置列表之上,而不是替换它,因此更改内置地址的版本仍能到达你这里。
- About — 插件和 swarmdrop 版本,以及检查是否有更新版本。

最后两项需要 swarmdrop 0.6.0 或更新版本;使用较旧版本时,它们会说明这一点,而不是向你显示参数解析错误。

面板无法打开此页面。 dsh 仅将 openSection 交给 settings.onboarding 条目,因此插件无法自行打开其所在部分的 Settings。面板会展开它已经就位的部分,并把打开 Settings 留给你——这就是为什么页面作为唯一归属的任何内容都必须只能从 Settings 中找到。

安装

dsh plugin --profile  add dsh-swarmdrop

这就是全部——该包声明了一个 dsh.bundle,因此 dsh 会将其追加到配置文件的 bundle 列表中,其配置层会在下次启动时激活。启动前使用 dsh --profile  --dump-config 验证,它应显示一个
== dsh-swarmdrop 层。

dsh plugin 会在 profile 目录内转发给 pnpm,因此它接受任何 pnpm 目标——无需 npm publish:

dsh plugin --profile  add /path/to/dsh-swarmdrop     # 本地检出
dsh plugin --profile  add ./dsh-swarmdrop-0.1.0.tgz  # 来自 npm pack

用 dsh plugin --profile  remove dsh-swarmdrop 移除它,这会同时移除依赖和该层。

SwarmDrop 二进制文件

你自己的安装优先。 如果 swarmdrop 在 PATH 上——Homebrew、安装脚本、npm i -g——那就是插件运行的版本。同时也会附带一份作为可选依赖,仅在你没有自己的版本时使用,因此 dsh plugin add 仍能给你一个可用的插件,无需安装其他东西。SWARMDROP_BIN 会覆盖以上两者。

顺序比看起来更重要。SwarmDrop 的数据目录是按用户划分的,最多只有一个进程为它持有节点;其他所有进程都会通过本地通道成为该节点的客户端,且没有版本协商。因此,无论哪个二进制文件启动了节点,就决定了什么能正常工作,而运行一个与你终端不同的二进制文件,正是你最终得到一个自己的 swarmdrop 无法与之通信的守护进程的原因。遵从 PATH 能让两者使用同一个二进制文件。

About 部分会说明正在使用哪个二进制文件、它来自哪里,以及运行中节点的版本——当两者不一致时会给出警告。即使没有两个安装,这种情况也会发生:swarmdrop update 会替换可执行文件,并让守护进程继续运行旧代码,直到它被重启。

你会在安装过程中看到 pnpm 提示 Ignored build scripts: swarmdrop。这没关系:npm 包通过 postinstall 钩子获取其平台二进制文件,pnpm 默认会阻止这些钩子,而 shim 会改为在首次使用时回退到获取。唯一可见的影响是安装后第一次调用 SwarmDrop 会比其余调用多花几秒钟——而且只有在使用捆绑副本的情况下才会如此。

需要 swarmdrop 0.9.0 或更新版本。 0.4.0 添加了 swarmdrop watch,本插件会订阅它;0.5.0 添加了 invite create --decide-from-stdin,正是它让面板能够运行配对台;0.9.0 添加了 invite qr,配对对话框中的代码就来自它。下限并不统一:低于 0.5.0 时插件不起作用,而在 0.5.0 到 0.9.0 之间,一切都能工作,配对也能工作——只是对话框会说它无法绘制代码,并留给你链接。

从面板配对设备——否则插件没有可通信的对象。如果你更喜欢,终端方式仍然可用:

swarmdrop invite create      # 打印链接;打开它以获取可扫描的代码

这里的一切都不要求 SwarmDrop 节点正在运行:在你尚未启动节点的机器上,插件也能正常加载,工具会说明这一点,而不是神秘地失败,并且面板会提供启动一个节点的选项。

工具

| 工具 | 作用 |
|---|---|
| swarmdrop_send_files | 将文件或目录发送到你的某一台设备。 |
| swarmdrop_send_text | 向某台设备的收件箱发送一条短消息。 |
| swarmdrop_list_devices | 你已配对的设备,以及它们是否在线。 |
| swarmdrop_node_status | 本地节点是否正在运行,以及如何访问它。 |
| swarmdrop_list_inbox | 你的设备向这台机器发送了什么,以及它们落在了哪里。 |
| swarmdrop_search_inbox | 按关键词查找条目——标题、发送者、消息正文、文件名。 |
| swarmdrop_inbox_item | 完整查看一个条目:每个文件的真实路径,或消息正文。 |
| swarmdrop_inbox_files | 只查看某个条目的文件,当你只需要这些时。 |
| swarmdrop_list_transfers | 传输会话,进行中的和最近的。 |
| swarmdrop_transfer_status | 单个传输:阶段、进度、速率。 |
| swarmdrop_pause_transfer | 暂停正在传输字节的传输。 |
| swarmdrop_resume_transfer | 从检查点恢复。 |
| swarmdrop_cancel_transfer | 永久停止一个传输,并告知另一端。 |

这里没有任何东西可以配对设备。 接受入站请求是你在面板上做出的决定——一个能够配对的工具,就是一个能够把进入这台机器的通道交给陌生人的工具。

有两个值不是布尔值而是三值,而且这两个区别都很重要:

- presence 是 online / offline / unknown。unknown 意味着没有正在运行的 SwarmDrop 节点可供探测——这就是“你的手机在休眠”和“启动 SwarmDrop”之间的区别。
- 传输的 speed 是一个数字或 null,绝不是 0。核心对于“滑动窗口内没有新字节”会报告为零,而这正是保存已完成文件时的样子;把它当作测量到的零传递出去,会让 agent 告诉你一个健康的传输已经停滞。

swarmdrop_search_inbox 需要 swarmdrop 0.7.0;较旧的版本会用一句话说明这一点,而不是像你输错了什么一样失败。

每次调用都会在其卡片上说明它正在做什么——Send 3 files to 光印-华为410,而不是在参数转储之上显示 swarmdrop_send_files。实时进度和暂停 / 取消控件不在卡片上:dsh 的工具卡片是一组封闭的静态形状词汇,它们的呈现器是纯函数,会在数月后被重放,因此卡片无法诚实地表示“此刻”。这些内容存在于对话行和面板中,而这两者都可以做到。

它是如何组合起来的

src/
cli.ts         swarmdrop 二进制文件:一次性调用、订阅、配对
machine.ts     这台机器看起来是什么样,由订阅折叠而来
pairing.ts     配对工作台:一个窗口,以及谁站在它前面
revision.ts    面板所依赖的共享“有东西变了”计数器
bridge.ts      机器范围内的事件  →  按会话划分的事件
panel.ts       面板的 RPC 通道(状态、设备、配对)
panel-wire.ts  面板的通信契约,由两半共同编译
console.ts     设置页面的两条路由:读取一个区段,运行一个操作
console-wire.ts  页面的通信契约,由两半共同编译
projection.ts  收件箱列表,作为 Session 投影(@ 读取的内容)
tools/         模型可以调用的内容
index.ts       注册表:每个工具,只注册一次
shape.ts       CLI 输出  →  本插件的契约
explain.ts     CLI 失败 →  模型的下一步动作
send.ts        写入对话的那两个
inbox.ts       收到了什么,以及在哪里
transfer.ts    观察一个,并操控一个
device.ts      谁可达,以及这个节点是否在线
present.ts     一次调用运行期间及之后,其卡片显示什么
command.ts     你可以输入的内容
types.ts       本插件拥有的 Session 事件族
client/        浏览器那一半:面板、对话行、@ 数据源

在改动任何东西之前,有四个值得了解的决定:

两类数据,两种载体。 对话行和 @ 候选项通过会话日志传递:它们必须在刷新、翻到历史页、或几个月后重放时都能以相同方式重建,因此 @ 菜单读取的是会话投影——Node 那一半注册一个纯折叠,框架按日志顺序在已提交事件上驱动它,浏览器收到的是一个最终值。

面板的数据不走那里。节点存活状态、设备和网络态势是关于当下的事实;一个声称“节点在线”的会话事件会成为一个关于某一时刻的断言,被永久持久化,并在读回时仿佛仍然为真。因此面板有自己的通道——Host 上的 ctx.connection.rpc.handle('/swarmdrop', …),浏览器中的 rpc.call。两者在所有 dsh 载体下都能工作,包括从你的手机访问家里的 dsh。

本文件较早的一个版本说 dsh 不给第三方插件提供 Client→Node RPC。那是错的。真正成立且关键的是上面的拆分:转录从日志重建,任何东西都不得绕过这一点。

面板进行长轮询,因为无法向它推送。 dsh 从一份固定的允许列表将 Host 事件转发到浏览器,第三方插件无法扩展该列表。因此面板在 Host 上停放一个请求,直到有东西发生变化——这并非推送的降级:当变化发生时,请求已经在等待,所以答案会立即发出,而不是等到计时器的下一个 tick。

事件记录发生了什么,而不是现在是什么。 swarmdrop/sent、swarmdrop/inbox-received 和 swarmdrop/transfer 是在某一时间点发生的事,因此几个月后重放一段对话仍然能解释它。唯一一个整值事件 swarmdrop/inbox-baseline 回答的是“这件事开始时你手头有什么”——这正是读者需要的上下文。

面板进行长轮询;页面完全不轮询。 面板可以承受一个停放的请求,因为 Host 会保持它打开。设置页面上的任何东西都做不到:每个 section 都是一个 swarmdrop 进程,所以 section 在你打开它时才会被读取
而当你询问时,实时的那一半(节点存活状态、设备、配对)根本不会被重新读取——它已经通过面板的订阅到达,页面读取的是同一个存储。

每个负载都携带一个 version。 这些会进入你的会话日志,而会话日志的生命周期长于进程,并且会被重放。一种仍然能解析但含义不同的格式变更,是可能发生的最糟糕的失败。

一个你应该知道的限制

dsh 拒绝读取包含它不认识的事件类型的会话日志,除非该事件被标记为 ignorable。这两种规避方式对第三方插件都不可用:已知类型集合是根据 dsh 仓库内部声明的类型生成的,而 Session.append() 没有提供任何设置该标记的方法。dsh 知道这一点——它自己的源码说,面向仓库外事件的注册接口“被推迟到此类消费者出现时”。

这个插件就是那个消费者,所以它在加载时会向正在运行的 harness 宣告它的四种事件类型。这使得它们在这里可读。它不会在事件上设置 ignorable,所以:

请禁用这个插件,而不是卸载它,如果使用过它的对话对你仍然重要的话。一个没有该插件的 harness 会拒绝打开包含其事件的会话日志——你会看到“unknown to this harness”,并且整个对话,而不仅仅是 SwarmDrop 行,都会变得不可读。

该宣告还依赖于插件和 dsh 解析到同一个 @deepseek-ai/dsh-session 模块实例。这在普通安装中成立;当 dsh 在 tsx 下从源码检出运行时则不成立,此时两半会得到独立的模块图,宣告会落在一个没人读取的副本上。

开发
bash
npm install
npm run typecheck   # both halves
npm run build       # tsc for the Node half, tsdown for the browser one

这两半会作为独立的 TypeScript 程序编译,而这不是可选的。 dsh 在两侧对 Context.sessions 的增强方式不同(Node:SessionStore;浏览器:ISessions),所以把两者放进同一个程序会让浏览器那一半针对 Node 服务接口进行编译,并失败于指向完全偏离原因的错误。同样的规则也适用于源码内部:客户端文件绝不能导入包根——只能导入 /types 和 /client 子路径,它们不携带 Context 增强。

浏览器 bundle 不是普通的 ESM 构建。 dsh 的加载器期望 ./client 入口通过 window.__ModuleLoader__.load({ id, factory }) 注册自身,并通过注入的 require 解析外部依赖——没有 import map,也没有全局变量。dsh 用一个未发布的共享 tsdown 预设来构建它自己的版本,所以 tsdown.config.ts 以 banner/footer 对的形式重新实现了这个包装器。id 必须等于包名,因为那是宿主组合进 window.__DSH_BOOT__ 的入口名。

关于那个配置,有两点是承重性的,而非风格性的。tsdown 在 tsc 之前运行,而不是之后,正是这个顺序让它能够 clean:
lib 同时容纳两半,且没有其他东西会清空它,所以在关闭清理的情况下,三
个提交前删除的源文件所产生的输出仍会留在那里,而 files 会把它打包进去。如果
第二个运行 tsdown,同样的 clean 反而会删除 Node 那一半。而 banner 以对象形式
({ js: … })给出,是因为普通字符串会被前置到每一个* chunk:声明文件也会被加上
包装,从而不再能作为 TypeScript 解析。

两半的声明来自不同的工具,这就是它们落在不同位置的原因:lib/types/* 逐文件
镜像 Node 源码,而浏览器那一半是单个打包后的 lib/client.d.ts。两者都是
exports 指向的目标;两者都不是手写的。

有三件事,否则读者会以艰难的方式重新发现:

- 对话节点 cookbook 的代码片段按原样无法编译。
ChatNodeViewProps 捆绑了 t: TranslateNS,但只有当注册传入
locale 时,插槽才会注入 t,而第一方代码传入的命名空间值并未导出。参见
src/client/nodes.tsx。
- exec.agent 是可选的。 嵌套的 Code-Mode 派发没有 agent,因此发送仍会发生,
但没有可归属的对话。
- npm 的 latest 标签滞后于真实版本线。 npm view @deepseek-ai/…
报告的是 0.0.1-rc.1,其客户端包依赖 @deepseek-ai/dsh-compact
—— 一个未发布的包,使该版本线无法解析。实际在用的版本线是 0.1.0-rc.x,它可以
干净地解析。请检查 npm view  versions,而不是裸的 version。

发布

changelog 是发布的输入,而不是发布之后写的记录。先写章节,再打标签:
bash
1. 在 CHANGELOG.md 中,在新的 ## [x.y.z] - YYYY-MM-DD 标题下描述变更,
并在文件末尾添加其 compare 链接。提交它。
2. 让 npm 设置版本,提交并打标签。
npm version minor
git push --follow-tags

用 npm version 设置版本,绝不要通过编辑 package.json。 它也会写入
package-lock.json,而手工编辑的 manifest 会把 lockfile 落在后面 —— 这会一直不可见,
直到之后的某次 npm ci 拒绝安装,并把发布任务一起拖垮。发布任务正是因此会检查两者
是否一致。

推送标签会运行 release.yml,它会类型检查两半、运行测试、拒绝与 package.json
不一致的标签或与任一方不一致的 lockfile、从 CHANGELOG.md 中读取说明 —— 如果该版本
没有章节则失败* —— 以 provenance 发布到 npm,并用同样的说明创建 GitHub Release。

npm 会在 Release 创建之前发布,因为 npm 是无法撤回的那一半。因此,唯一值得知道如何
修复的失败,是在发布之后才死掉的运行:不要重新运行该任务,那会尝试发布一个已经
存在的版本。改为手动创建 Release:
bash
node scripts/changelog-section.mjs x.y.z > /tmp/notes.md
gh release create vx.y.z --title vx.y.z --notes-file /tmp/notes.md --verify-tag

普通推送和拉取请求会运行 ci.yml——相同的类型检查和测试,
外加一次真实的 npm run build,因此浏览器打包的包装器会在
npm publish 之外的地方得到实际检验。

许可证

MIT

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群