v1.4.0
[1.4.0] - 2026-08-24
Compatibility (通用插件共存保护)
- 修复:与其他远程/Web 接入类插件共存时启动致命崩溃。
remoteWebUiPairing是远程/Web 接入类插件的通用配对共享服务名,任何其他插件先注册同名服务后,本插件再注册必然抛错;而 loader 以Promise.allSettled并发激活全部 entry,任一失败即回滚整棵插件树并使进程退出(市场安装后直接启动即崩,此前只能手动禁用对方解决)。现改为延迟裁决:等待激活窗口结束后检测服务名归属——已被占用则主动让出(不注册、不抛错),无人注册才接管,保证共存时必定可正常启动。 - 新增:共存状态可见化。裁决结果通过
/api/remote-mobile/status的pairingBridgeMode(active/yielded/pending)、pairingBridgeConflict与pairingBridgeConflictId下发(基于 Cordis 公开运行时结构尽力定位占用方包名与 entry id,无法识别时前端回退通用文案);设置页顶部在共存时展示可关闭的警示横幅(中英文),支持一键复制「诊断与修复报告」(含精确 entry id 与可直接执行的修复步骤,可直接粘贴给 AI 助手处理);host 日志同步输出 warn。判定逻辑完全通用,不针对任何特定第三方插件。 - 文档:澄清 EADDRINUSE 成因。启动报错
listen EADDRINUSE: address already in use 0.0.0.0:3080是端口被其他进程占用(典型:上一个 dsh 实例未退出、桌面快捷方式隐藏实例),与安装哪个插件无关,README FAQ 新增排查步骤(Q0)。 - 配置说明调整:「禁用其他远程接入插件」由必选改为推荐项——共存不再导致启动失败;保留其一仍是最佳实践(避免功能重复与入口混乱)。
Fixed (缺陷修复)
- 修复:手动切换扫码页签后被强制切回 Tailscale。设置面板每 3 秒轮询一次状态,只要本机存在 Tailscale IP 就会强制把扫码页签切回 Tailscale,用户手动选中的「局域网扫码」数秒内被覆盖。现记录用户的手动选择:自动预选仅在用户从未手动切换时生效。
- 修复:移动端样式片段启用后设置弹窗被困在侧边栏内。设置面板内联渲染于侧边栏 DOM 中,其全屏遮罩层为
position:fixed;而官方在侧边栏列上遗留的恒等 transform(matrix(1,0,0,1,0,0))会把 fixed 后代的包含块困在该列内,叠加官方overflow:hidden后弹窗被裁剪成抽屉宽度(表现为「弹窗出现在侧边栏里面」)。preset-sidebar现显式清除侧边栏列上全部包含块触发属性(transform/filter/backdrop-filter/perspective/will-change),并将 body 直挂的模态 portal 容器统一抬升层级,两类弹窗均恢复正常全屏居中。 - 修复:移动端侧边栏抽屉内外层宽度不一致。官方响应式脚本会给内层根节点写入固定内联宽度(如
280px),与外层抽屉实际宽度(官方min(88vw, 320px))不一致,在抽屉右缘留下约 40px 无法交互的空白条;现强制内层根节点铺满抽屉(width:100% !important覆盖内联样式),内外对齐无死区。 - 修复:移动端侧边栏抽屉层级过高压住官方弹层。抽屉原采用
z-index:99999,高于官方模态弹窗(1000)与下拉菜单(1100,portal 到 body),导致设置面板的操作菜单弹出后被盖在抽屉之下无法交互。现整体下调:抽屉900、悬浮把手901——仍稳定覆盖正文内容,同时让位官方全部弹层。 - 修复:官方 frame::after 全屏点击遮罩罩住抽屉内容。抽屉降层级后,官方窄屏抽屉的全屏遮罩(
[data-dsh-frame]::after,fixed、rgba 黑纱、z-index:1050、可拦截点击)反过来压在抽屉之上;现将其压制到899——低于抽屉、高于正文,暗化背景与「点外部收回抽屉」能力保留。 - 修复:移动端抽屉内点击次级操作图标(更多操作 ⋯ / 展开箭头等)后抽屉被误关。生态插件(如 @linxin666/dsh-web-ui-all)的「点条目自动收回抽屉」增强会把条目内
role=treeitem下的所有点击一律视为选中,下一帧程序化点击收起按钮,导致 ⋯ 菜单未弹出抽屉先被收起。现注入防误收起守卫:记录对条目内按钮/箭头的触摸,900ms 内的程序化收起调用被忽略;真实点击收起按钮、点条目主体回主区的官方行为均保持不变。 - 修复:触屏点击展开把手后官方 Tooltip 气泡永久滞留(黑底「收起侧边栏」)。官方气泡显示依赖
mouseenter/focus、消失只认mouseleave/blur,触屏点击后这两个事件永远不会到来;现移动端 UA 下整体隐藏官方 Tooltip 气泡(按钮均保留aria-label,可访问性不受影响),PC 窄窗口悬停提示不受影响。
[1.3.1] - 2026-08-24
Security Hardening (安全加固)
- 回环 CSRF / drive-by 防御:变更类插件 API(非 GET 的
/api/remote-mobile/*)在门禁层校验浏览器同源信号——Origin与 Host 不一致、Origin: null或Sec-Fetch-Site: cross-site的写请求直接返回 403。恶意网页无法再驱使已登录用户的浏览器(现代浏览器对跨站请求必携带Origin/Sec-Fetch-Site信号)向127.0.0.1发起免认证的跨站写操作(如开启局域网免密、清除密码、注销设备)。curl / 本机脚本等不带浏览器信号的客户端保持完全兼容;本机面板与手机端均为同源请求,行为不变。 - 回环上下文不再洗白:Host / Origin / Sec-Fetch-Site 虚拟化改写仅作用于外部来源流量;本机回环 socket(127.0.0.1 / ::1)保留原始头,仅将
localhost/[::1]变体归一化为127.0.0.1;socket.remoteAddress的统一虚拟化仍保留(仅对外部来源流量产生实际效果——真实回环请求本就是127.0.0.1,该改写为 no-op)。此前对全部请求统一改写会顺带拆掉底层 WebServer 自带的 DNS-Rebinding 防护,恶意域名解析到 127.0.0.1 时可借道读写本机接口,现已恢复该防护对外部域名的效力(外部合法流量的虚拟化放行不受影响)。 - SSE 事件流收紧:移除
/api/remote-mobile/events响应中的Access-Control-Allow-Origin: *。前端 Bundle 与接口同源,无需 CORS 头;该头曾允许任意网页通过 EventSource 持续读取设备接入与安全告警事件流。 - devices.json 权限收紧为 0o600:会话持久化文件内含 365 天长效访问 Token(即完整凭证),落盘权限与
rsa-keys.json对齐,仅当前用户可读写(已有旧文件会在下次写盘时自动修正)。
[1.3.0] - 2026-08-23
Mobile Style Snippets (移动端样式片段 / 样式小插件)
- 全新模块
src/styles/style-snippets.ts:插件内部独立的「样式小插件」注册表,将 DSH 界面样式拆分为可独立启停的 CSS 片段。 - 按界面区域划分的三段内置预设:
preset-sidebar侧边栏抽屉导航(折叠 0 宽度 + 可拖拽悬浮把手)、preset-settings设置面板适配(弹窗居中微缩 + 遮罩锁滚动)、preset-main对话正文高密度排版(小字号 12.5px + 紧凑行距 + 收紧边距,每行展示更多内容;字号规则基于稳定 HTML 元素与 CSS Modules localName 后缀,不做布局缩放——zoom 会挤压 1fr 网格轨道产生右侧空白甚至破版,已弃用),默认移动端启用、PC 关闭。 - PC / 移动端分别启停:每个片段(内置与自定义)均支持
pcEnabled/mobileEnabled双开关,注入层按视口宽度档选择对应端生效(窄屏 ≤900px / 宽屏 >900px,详见下条);存储升级为 v2(presetStates+ 双端字段)并自动迁移 v1 旧数据。 - 内置预设状态分组齐全落盘:
presetStates恒含preset-sidebar/preset-settings/preset-main三个分组,每组pc/mobile写当前有效值(用户覆盖 ?? 代码默认值),文件自包含、三组一目了然。 - 设置面板页签分组导航:远程与移动端设置按「接入 / 设备与安全 / 样式覆写 / 本地数据」四个页签分组,消除长页面;全组保持挂载、仅 display 切换,切页不重置配对码/表单状态,页签选择 localStorage 记忆。
- PC / 移动端开关改为按视口宽度生效(不再按设备 UA 过滤):
mobileEnabled的片段包@media (max-width: 900px)(窄视口生效,PC 浏览器拉小窗口同样生效),pcEnabled包@media (min-width: 901px)(宽视口生效),两端都开则全宽度;内置预设 CSS 不再写死媒体查询外壳,由注入层按开关包装;修复了此前「只勾移动端时 PC 拉小屏幕不生效」的问题。 - 用户自定义片段:支持在设置面板(设置 ⚙️ → 远程与移动端 → 🎨 移动端样式片段)或 API(
GET/POST /api/remote-mobile/styles等)中动态新增/编辑/删除/启停,持久化于~/.dsh/remote-mobile/style-snippets.json,无需修改插件代码、无需重启。 - 按 UA 注入 +
data-dsh-mobile标记:网关响应层按 User-Agent 判定移动端,向主工作区 index.html 与 /auth 登录页注入<style id="dsh-remote-mobile-style-snippets">,并给<html>打上data-dsh-mobile属性作为稳定作用域选择器钩子;桌面端启用片段时同样按需注入(建议片段不依赖data-dsh-mobile作用域)。 - 悬浮把手拖拽脚本(全端注入 + 折叠状态自守卫):
dsh-draggable-nav支持拖动侧边栏展开把手并记忆位置,配合preset-sidebar使用;脚本对所有请求注入,运行时仅在侧边栏处于折叠状态时生效(桌面端未折叠时不工作,PC 窄窗口拉出折叠侧边栏时同样可拖动)。 - 存储卡片展示真实跨平台绝对路径:设置面板「本地数据与密钥持久化存储说明」中的三个文件路径(devices.json / rsa-keys.json / style-snippets.json)改由
/api/remote-mobile/status动态下发(服务端用join(homedir(), ...)跨平台拼接),不再硬编码~/.dsh/...形式。 - 构建脚本健壮性:
scripts/build-client.js自动同步到 DSH 运行目录失败时仅告警不再中断构建。 - 修复:
beforeExit兜底落盘真正生效:StyleSnippetStore构造函数补充activeStores.add(this)(与SessionStore保持一致),此前文件末尾的遍历集合恒为空(死代码)。 - 修复:样式路由双注册冲突:
/api/remote-mobile/styles收敛为「单 path 内按 method 分发」,规避 webserver 精确路由同 path 重复注册冲突。 - 修复:自定义片段 id 冲突:拒绝使用内置预设 id /
preset-保留前缀作为自定义片段 id,避免列表、启停与删除语义歧义。 - 修复:内置预设名称本地化:
GET /api/remote-mobile/styles按请求语言返回预设名称/描述,英文界面不再显示中文。 - 加固:
Content-Length防截断守卫:注入<style>/Polyfill 改写响应体后、且响应头尚未发出时移除显式Content-Length,交由 Node 按最终 body 重新计算,规避ERR_CONTENT_LENGTH_MISMATCH截断风险(writeHead已发出头部属不可挽救场景)。 - 加固:
</style转义:buildMobileStyleTag将片段中的</style转义为</style的反斜杠形式(<\/style),防止用户 CSS 意外/恶意提前闭合<style>标签(与 dsh-host-webserver 对IndexInjection'style'行「text 不得包含</style」的约束一致)。
[1.2.1] - 2026-08-23
Documentation & UI Polish (文档与界面微调)
- 名称规范化:全项目与 npm 包元数据统一将 DSH 全称规范为 DeepSeek Harness。
- 配置说明优化:完善 README 中关于禁用内置远程插件的注释说明(防止功能冲突)。
- 设置界面精简:控制台设置页 GitHub 仓库徽标文字精简为
dsh-remote-mobile。
Full Changelog: v1.2.0...v1.4.0