Repository navigation
Releases
v0.5.1
-
补
dsh.compatibility.dshReleases声明:0.1.7-rc.2/0.2.0-rc.2/0.2.1-rc.5/
0.2.2-rc.1。依据:0.1.7-rc.2 上七插件实测加载;0.2.2-rc.1 的三条客户端断代 API
(EntryTree.write()→commit(change)、MenuTarget→ContextMenuTarget、
useAnchoredPosition/Menu.getAnchorRect移除)本仓引用数为 0。该字段供插件市场与
兼容性阅读器查看,不参与宿主加载判定 —— 宿主强校验的仍是peerDependencies。 -
修复 SMB 推送复制步骤从未成功过的致命 bug:
robocopy的参数被手工加了引号。
execFile不经 shell,参数里的引号会被原样交给程序,而robocopy自己不做引号剥离 ——
带引号的路径让它直接以exit 16(语法/参数错误)拒绝执行,一个文件都不会复制
(实测:带引号 exit=16、目标目录为空;裸参数 exit=1、文件正确落盘)。现改为传裸参数。
这条通道此前「挂载修好了、复制仍必然失败」,与 0.5.0 修的挂载 bug 是同一类的只在真机
才暴露的问题。 -
新增两条
robocopy实机用例与一条引号回归用例:单测过去只断言「参数构造」,证明不了
「文件落到了哪」。现在在 Windows 上真跑一次robocopy(纯本地临时目录,不联网),断言
落盘位置;CI 的 ubuntu / macos 组合自动跳过。 -
修复 SMB 推送单文件会多出一层同名目录:
robocopy的「目标」参数恒为目录,而单文件
分支把目标写成了<destDir>\<leaf>,于是文件实际落到…\<leaf>\<leaf>。现在单文件的目标
就是destDir本身、文件名作为第三个位置参数;目录分支不变(目标即<destDir>\<leaf>)。 -
设置项
sharePort/shareExpiresIn/sharePassword真正生效:三者此前只定义了 schema、
渲染了设置卡片、写进了 README,但 host 端引用数为 0 —— 改了不生效。现新增纯函数
resolveShareOptions()(显式参数优先 → 设置项兜底 → 内置默认),localsend_share与
localsend_send_plugin均改用它,并在入口配置里补齐三者默认值。 -
修复
expiresIn: 0(无有效期)在参数层不可达:旧实现写Number(x) || 3600,把显式传入
的0当成假值吞成了3600,与 README / 工具描述里「0 = no expiry」的承诺矛盾;port同理。
现在只有「未提供」才回落默认,0与空串按原样生效。 -
修复分享通道「调用后永久挂起」的致命 bug ——
localsend_share/localsend_send_plugin
此前从未真正可用过:createShareSession()用resolve({url,token,port,expires,files,close})
返回了一个新建的对象字面量,而closed/downloaded/onDone/temps只存在于内部
会话对象上。两个工具都写session.onDone = fn等下载结束 —— 回调落在另一个对象上,
shutdown()永远读不到,于是await永不返回。宿主侧也没有兜底:timeoutMs在 DSH 里
仅作声明、不强制执行,所以工具会一直挂到用户手动取消,而取消又会关掉服务器、只拿回一条死链。
实测:expiresIn: 5在 8 秒后onDone仍未触发,且返回对象上closed === undefined。
现在createShareSession()直接 resolve 会话本身;两个工具改为生成链接后立即返回,
服务器在后台继续服务直到过期或全部下载完成;临时 zip 改由会话持有、在会话关闭时清理
(此前由工具在finally里删除 —— 一旦改为立即返回,链接就会指向已被删掉的文件);
工具也不再绑定exec.signal,否则对话一结束服务器就被关掉。 -
分享链接改为根路径:多文件时
/dl/<token>只会给出第 1 个文件(实测响应体只含第 1 个),
现在对外给http://<ip>:<port>/—— 多文件渲染列表页,单文件自动 302 跳转到下载。 -
新增 6 条分享会话契约测试(
test/share-session.test.js):锁住「返回的是会话本身」、
onDone在「下载完成 / 到期 / 手动 close」三种情形下都会触发、临时文件随会话清理、
根路径渲染多文件列表页。此前grep onDone test/零命中 —— 50 个用例全绿也测不出这个 bug。 -
大目录打包改为流式写入,内存占用降一个数量级:
packDirectory()过去先由
collectDirectory()把每个文件的内容读进内存攒成数组,再交给createZip()一次性
concat 出完整 zip —— 整个过程中「全部原始数据 + 全部压缩结果 + 最终 buffer」同时驻留。
实测打包 60 MB 目录(20 个 3 MB 不可压缩文件)后仍驻留 375 MB。现在新增
writeZipFile()(lib/zip.js)逐文件「读入 → 压缩 → 写盘 → 释放」,collectDirectory()
只收集文件清单(stat,不读内容),同样输入驻留降到 21.5 MB。createZip()保留给
插件包等小体量输入。 -
zip 写入改用
FileHandle顺序写:先前的WriteStream版在「首个文件读取即失败」时,
异步 open 会在调用方结束之后才落地并抛ENOENT(实测触发uncaughtException);
改用fs.promises.open()+ 顺序write()后收尾干净,失败时也不会在磁盘上留下半截 zip。 -
分享链接不再指向虚拟网卡:
getLocalIp()旧实现取「第一个非内部 IPv4」—— 装了
WSL / Hyper-V / VirtualBox / Docker 时枚举顺序常把虚拟网卡排在前面,链接于是指向对端
根本访问不到的地址。现在新增getLocalIps()返回全部候选,getLocalIp()优先物理网卡、
跳过链路本地169.254.x,全是虚拟网卡时才退回第一个候选;localsend_share在多网卡时
会把所有候选地址打进日志,便于手动替换。 -
发送前增加可达性预检 —— 失败从 4 分钟降到 3 秒,并给出下一步:
localsend_send_files
过去对 IP 目标不做任何探测就直接进入 prepare-upload,于是「IP 打错一位」要等满
acceptTimeoutMs(默认 240 秒)才换来一句network error: timeout after 240000ms——
既没说哪一步失败,也没说下一步该做什么(实测确认失败耗时完全等于超时值)。
现在lib/discovery.js新增probeDevice():发送前对目标打一次/info,不通就立刻报错并
列出三条可操作路径(① 确认 LocalSend 已安装且正在运行 ② 确认两台机器在同一局域网
③ 改用localsend_share,接收方用浏览器即可,无需安装任何东西)。
实测不可达地址 3082 ms 返回,对比修复前的 240 秒。 -
协议不再硬编码 https:
probeDevice()先试 https 再试 http,并把探到的协议传给
sendFiles()—— LocalSend 默认用自签名 https,但 tailnet / 反向代理等环境可能只暴露
http,旧实现遇到这类接收端必然失败。实测「只开 HTTP 的接收端」8 ms 即被探到并正确
判定协议。 -
prepare 超时的错误文案改为说明真实原因:预检通过后对端必然在线,此时超时基本只剩
「接收端没点接受」一种解释,报错直接说明这一点并提示 LocalSend 可开启自动接受。
完整变更:CHANGELOG.md
兼容性:已验证 DeepSeek Harness 0.1.7-rc.2 / 0.2.0-rc.2 / 0.2.1-rc.5 / 0.2.2-rc.1。声明见 package.json 的 dsh.compatibility.dshReleases;宿主强校验的仍是 peerDependencies。
v0.5.0
- 新增「目标机档案」(
smbProfiles):设置卡片里可为每台目标机保存一条档案
(档案名 / 目标 IP / 共享名 / 共享下子目录 / 账户 / 密码),localsend_smb_push只需
profile="<名>"+files直推;任何显式参数(target/share/destDir/username/password)
按字段覆盖档案。把「无软件推送」从"每次重填 5 个参数"变成"配一次 + 一条命令"。
新增lib/smb.js的findProfile()(按名查找,找不到时列出已配置的档案名)与
mergeProfile()(显式参数优先、空值回落到档案)。 - 设置卡片新增档案编辑器:可增删档案行,六字段(密码用
password输入框);
保存前校验「档案名与主机必填、档案名唯一」,两端文案均双语。 - 修复
localsend_smb_push从未成功挂载的致命 bug:mountShare()曾把targetDir单独
拼一次,而buildNetUseArgs()的返回值里已含它 ——net因此收到两个 devicename 加上凭据
共 4 个位置参数,直接打印用法说明(语法错误)。带凭据与来宾登录两条路径都必然失败,
即该通道此前从未真正跑通。现改为net use <buildNetUseArgs 返回的完整参数>,并新增
「目标只出现一次」的回归用例;同时把net错误 67(找不到网络名)并入「找不到网络路径」提示。 - 安全约定更新:档案中的凭据(含密码)以明文保存在本机 DSH settings 文件里,会随该文件
被同步/备份而流转 —— 这是为「自己的多台机器之间推送」做的取舍。README 中英两版均已标注
「勿在共享/多人机器上启用」,并修正原先"不存储任何凭据"的无条件表述。 - 文档:README 默认语言改为中文并同步中英两版内容。 语言约定统一为
README.md(中文,默认入口)+
README.en.md(英文),两版顶部互链;README.zh-CN.md已并入README.md并删除。
同时修正中文版遗留的版本号、补齐localsend_smb_push与 peer 范围的准确描述,
使中英两版与当前包状态一致。files改为分发README.en.md。 - 脱敏:清除
lib/tools/smb.js参数示例与test/smb.test.js中的真实内网 IP 与主机名
(统一改用文档专用网段192.0.2.10与占位主机名TARGET-PC),并把test/unit.test.js中
疑似本机网卡地址归一为通用示例192.168.1.66。
完整变更:CHANGELOG.md
本条为历史版本补录:tag 早已存在,Release 于 2026-11-06 补建。
v0.4.1
- 修复设置卡片崩溃导致「内置插件」页签空白:卡片在
useEffect里调用face.localePreference(),
但注入的face只提供了readValues/saveValues,调用即抛TypeError,
整张卡片连同面板被 React 摘掉 —— 表现为在 设置 → 内置插件 里选中「LocalSend 局域网传输」后下方全空。
现在face提供localePreference()(优先宿主 locale 服务,缺失时回退浏览器语言),
调用侧同时加能力探测 +try/catch:旧版face缺这个方法时只损失语言切换,不再影响卡片渲染。 - 兼容性:不新增宿主 API 依赖,对 dsh 0.1.x 与 0.2.0-rc.1 均安全。
完整变更:CHANGELOG.md
本条为历史版本补录:tag 早已存在,Release 于 2026-11-06 补建。
v0.4.0
v0.3.1
- 修复
dsh web启动失败:localsend_share与localsend_send_plugin的 output schema 用了required: [...]数组,
宿主 dsh 的 value schema DSL 只接受逐属性required: true,插件树加载因此报
unsupported JSON schema: schema.required is not supported by the value schema DSL并中断启动。已改为逐属性声明。 - 修复会话历史损坏(resume 失败):
localsend_list_devices与localsend_send_files的output.render返回裸字符串,
而 dsh 契约要求返回内容块数组(ContentBlock[])。裸字符串被原样写进tool/result事件,导致该会话此后每次加载都报
SessionPersistenceCorruptionError: session event at seq N message must contain one tool-result block。
现已改为[{ type: 'text', text: String(value) }],与其余工具一致。 - 升级提示:0.3.0 及更早版本产生的历史会话若调用过上述两个工具,需先修复
tool-result.content(字符串包装为文本块)才能恢复加载。
完整变更:CHANGELOG.md
本条为历史版本补录:tag 早已存在,Release 于 2026-11-06 补建。
v0.3.0
feat: 0.3.0 - localsend_send_plugin (self-contained plugin package ov…