# 更新日志 本文件记录 dsh-workspace 各版本的主要变化。开发中的改动先写在「未发布」下,发布时(`npm run release`)自动定稿为对应版本。 ## 未发布 ## 0.12.0(2026-09-30) - 新增:远程会话的 Agent 工具 `upload_to_remote`,把对话里的图片 / 文件(用户粘贴的,或 `generate_image`、`read_image` 等工具产生的)上传到远程工作区。只在远程会话注册,本地会话的工具清单不变。 - 来源:只接受 `attachment_id`(`sha256:…`),并且必须是本会话里出现过的附件。附件经 DSH 附件服务读取,写入生效前核对内容哈希等于 id。**不接受任何本机路径**,这个工具不能被用来把本机文件外传到远端。 - 来源里的生图结果:只认 `generate_image` / `edit_image` / `get_image_generation_task` 这三个工具结果里的 `meta.images`,其他工具结果里的同名字段不算,防止被伪造的引用扩大范围。图片在写入时才读取。 - 目标:只接受相对工作区根的路径,拒绝绝对路径、`..`、`~`、反斜杠、以 `/.` 结尾的写法、控制字符(含 C1),以及双向控制符、零宽字符等会让显示与实际不一致的字符。 - 在远端用 SFTP `realpath` 按真实路径校验,工作区内的目录链接允许,指向工作区外的一律拒绝。 - 目标本身是符号链接时拒绝,不跟随。 - 临时文件独占创建(O_EXCL)后再改名,改名前与写完后各复核一次;临时文件名里的原文件名按字节截断,不会超过 255 字节上限。 - 覆盖时优先原子改名;只有服务端不支持该扩展时才退化为「删旧 + 改名」,权限不足等真实失败直接报错,不会丢掉原文件(远程编辑器保存也一并修正)。 - 受保护位置:任意层级的 `.git/`、`.ssh/`、`.gnupg/`、`.husky/`,以及 `authorized_keys`、`.envrc`;远端家目录下紧接的点文件和点目录(`.bashrc`、`.config/autostart/…`),按真实路径判断,工作区根是家目录本身还是它的上级(`/home`、`/`)都拦得住。 - 新建文件不带可执行位(请求 0644,实际权限再受远端 umask 约束);覆盖会替换整个文件,原文件的属主和权限不保留。默认不覆盖(需显式 `overwrite`)。单文件上限 100 MB。 - 同一会话的上传串行执行;排队中的调用可以被中止;前一个上传卡住超过 10 分钟后,不再挡住后面的上传。 - 上传成功后才把覆盖前的原内容存入改动前存档(与 write / edit 共用)。上传后对该文件的「已读」登记同时覆盖模型原来的写法和真实位置。 - 每次调用(包括被拒绝的)都写一条连接日志,不含文件内容;日志里的外部输入经过转义并截断,不能伪造出新的一行。 - 注意:DSH 保存图片附件时会做归一化,传到远端的是 DSH 存档的那一版,不一定和原始文件逐字节相同。 - 新增:远程会话里 `present` 支持远程文件,交付卡片和收尾正文里的文件链接会在侧栏打开远程预览。 - 原因:宿主的 `present` 用本机文件系统检查路径,远程路径一律报 file not found,卡片出不来。 - 做法:在远程会话里接管 `present`,在远端检查文件存在、是普通文件、不是符号链接;工具成功返回后,按宿主格式追加 `deliverables/presented` 事件(核对版本 0.2.0-rc.2),卡片界面完全复用宿主的。 - 路径统一记为规范化后的远程绝对路径;相对路径和照抄占位目录的路径都会换算过去。 - 一次调用里全部是本机文件时(如生图后复制到本机的预览图),交回宿主原实现;本机与远程混在一起时报错,提示分两次调用。 - Windows 宿主:盘符 / UNC 路径是本机文件。macOS / Linux 宿主:`/` 开头又不在远程工作区根下的路径,本机存在且远端不存在时按本机文件处理(例如 `/Users/me/pic.png`)。 - 插件写入的交付事件给每个文件加上 `remoteHost` 标记,▾ 菜单的服务端查询以它为准,与 present 时的判断保持一致。 - 卡片右侧 ▾ 菜单对远程文件改为:菜单里的应用只有「侧栏预览」(默认),「打开」会在侧栏打开远程预览;「显示文件位置」会打开插件的「文件管理」,逐级展开到该文件,高亮并滚动到它所在的行(路径含隐藏目录时自动显示隐藏文件)。之前这两项都会报「无法在文件管理器中显示」。 - 实现:▾ 控件直接调用全局 `fetch('api/present.open?…')`,宿主没有可替换的回调,所以在浏览器端包装 `fetch`,只处理这一个路径、并且只处理远程工作区会话里的远程文件。 - 其余请求,包括本地会话的,以及远程会话里交付的本机文件,一律原样交给宿主;查询失败也交还宿主。 - 新增网关方法 `presentedFile`:按「会话 + 事件序号 + 文件下标」读交付事件,判断该文件是不是远程文件、远程路径是什么。 - 文件不在工作区根下,或当前显示的会话属于别的工作区时,「显示文件位置」返回宿主的「没有可用的主机路径」提示,而不是假装成功。 - 接管了 DSH「文件」侧栏时,定位到接管的那个标签,不会出现两个文件树。 - 标签重新挂载(切换会话后回来)时不会重复执行定位。 - `fetch` 包装全局只装一层,插件热更新时不会层层叠加。 - 菜单里「显示文件位置」及执行后的提示文字来自宿主,实际行为是在插件「文件管理」里定位,文字对不上属于宿主控件的限制。 ## 0.11.1(2026-09-30) - 修复:「添加工作区」弹窗里在连不上与连得上的主机之间来回切换会卡住,连带正常主机与状态轮询一起排队直到超时。 - 原因:连不上的主机每次请求要挂约 107 秒(5 次 × 20 秒握手超时重试),切走后请求也不会撤回;挂着的请求占满浏览器对同一地址的并发连接(HTTP/1.1 每个来源 6 个),后面的请求全部排队。 - 主机连不上(拒绝连接、不可达、域名解析失败、握手超时)不再重试:拒绝连接立即失败,不可达最多等一次握手超时(默认 20 秒);连接中途断开仍按原策略重试。 - 弹窗里同一台主机的家目录请求复用进行中的那一次,成功后记住,来回切换不再重复发请求。 - 页面状态轮询:上一次还没返回就不再发新的,不再在排队时越堆越多。 - 「新建终端」不再因为一台主机正在连接而整个禁用。 ## 0.11.0(2026-09-30) - 新增:全局配置里的「更新」卡片。 - 检查更新:先直连 GitHub(`releases/latest` API,失败时改读发布页跳转地址),再不通回退 npm 官方源(`registry.npmjs.org`,不用同步滞后的 npmmirror)。 - 下载并更新:安装包下载到 `~/.dsh/workspaces/updates/`,按发布页的 sha256(npm 来源用 sha512 integrity)校验,再检查包名、版本与插件声明,然后交给 DSH 插件管理器安装。 - 安装不先卸载:先卸载会把插件自身的后端停掉,后续安装就没人执行了。 - 安装包文件名每次带时间戳,避免宿主报 `ambiguous-install`。 - 上传离线包:上传 `.tgz` 后先检查,显示版本、大小、sha256,以及相对当前版本是新、同版还是降级;确认后才安装。 - 安装完成后提示重启:桌面版提供「立即重启 DSH」按钮,其他环境提示手动重启。 - 未打包的开发构建不检查更新;GitHub 访问频率受限时有提示。 - 升级完成后提示重启:在全局配置里看着安装完成时弹窗「升级完成」(桌面版可直接「立即重启 DSH」或「稍后重启」,网页版提示手动完全退出再打开);更新卡片顶部常驻醒目的待重启提示条(刷新页面后仍在),直到重启加载新版本 - 修复:在「远程工作区」页面安装新版本后,宿主会重载插件的浏览器端代码、把页面关回会话,「升级完成」提示随之丢失,要手动再进来才看到。现在开始安装时在浏览器里留下标记,重载后自动回到全局配置并弹出重启提示(弹过即清,30 分钟后失效;后台已是新版本时静默清除) - 新增:「远程文件」「远程 Git」改名为「文件管理」「Git 仓库」,并支持本地工作区。 - 本地会话直接读写本机文件、执行本机 git。 - 根目录由宿主按会话 cwd 推导,浏览器只传会话 id。 - 路径限定在工作区内(含同仓库的其他 git 工作目录);词法检查后再做 realpath 校验,拒绝经符号链接或目录联接逃出工作区。 - 按用户决定,本地写操作不受 DSH 会话沙箱策略限制。 - 新增:文件树右键菜单补齐新建文件、新建文件夹、上传到此处、下载、删除(远程与本地都有);名称输入与删除确认改用侧栏内浮层(桌面版不支持 `window.prompt`)。 - 新增:接管 DSH 自带的「文件」侧栏(kind `files`,含 Ctrl/⌘+P),改开「文件管理」。 - 在全局配置里可以开关,默认开启。 - 若已被其他插件(如 better-sidebar)接管则不抢,只在全局配置里提示去关闭它的「文件」标签;关闭后自动接管,无需刷新。 - 升级后只刷新了页面、DSH 后台仍是旧版本时,本地工作区的「文件管理」「Git 仓库」直接提示完全重启 DSH,不再报「主机不存在:local:…」 - 「文件管理」「Git 仓库」的左侧列表与右侧预览各自滚动:会话顶部标签的宿主视图区高度随内容增长,原来列表与预览会被外层会话容器一起滚动;现在面板高度钉成可视高度(扣除上方内容与输入框),宽屏两列各自滚动 - 文件树、改动列表(含查看分支的改动、与当前分支比较)改为虚拟滚动:只渲染可见区附近的行,5000 个文件 / 3000 条改动实测只渲染约 40 行;分区标题(已暂存 / 更改 / 未跟踪)滚动时吸顶 - 重构:远程 Git 的执行方式改为「参数数组 + 执行器」(远程 = 逐个单引号转义后经 SSH,本地 = 直接起 git 进程、不经 shell)。 - 所有 git 命令统一加 `-c core.fsmonitor=false`,防止恶意仓库借 fsmonitor 执行命令。 - 新增用本机 sh 代替 SSH 的执行器测试。 ## 0.10.1(2026-09-30) - 修复:宿主目录选择能力为 native(macOS 桌面版,只能弹系统对话框)时,「添加工作区」的本机一栏直接显示 `directory-picker/unavailable ... serves "native"` 报错。现在改由插件宿主端自己列本机目录(新增远程方法 `localList` / `localMkdir`,Node fs 实现,只列子目录),macOS 上也能应用内浏览、新建文件夹;「使用系统对话框…」按钮仍保留(取用顺序与官方 native picker 一致) - 修复:桌面版远程文件侧栏的 HTML / Markdown 预览白屏或报 `Browser access is disabled`。桌面版本机 Web 服务只放行 DSH 自身页面来源的请求,而预览 iframe 刻意不带 `allow-same-origin`(远程 HTML 不可信),它的请求拿不到放行令牌。桌面版改为 srcdoc 预览:页面里的相对样式表 / 脚本 / 图片 / CSS `url()` 与 `@import` 经新增的远程方法 `sftpReadData` 取回后内联(单个资源上限 8 MB),iframe 仍是不透明来源;网页版保持原来的预览路由。已知限制:脚本运行时 fetch / 动态加载的相对资源不可用 - 修复:Monaco 编辑器样式丢失时(macOS 反馈:代码行乱飘、多出一个原生输入框)不会自己恢复——monaco.js 只执行一次,样式标签被移除后就再也不注入。现在构建产物把 CSS 全文存到 `window.__dshwsMonacoCss`,每次创建编辑器 / 对比视图前核对并补回(也兼容挂在 Shadow DOM 里的情况) - 桌面版预览内联增加兜底:`sftpReadData` 失败(典型是宿主端仍为旧版本、新方法 404)时,样式表 / 脚本 / SVG 退回用 `sftpRead` 按文本读取,页面不再整页丢样式;仍失败的资源在预览上方提示(含「请用 ⌘Q 完全退出」),不再静默 - DSH NEXT(页面为 `dsh-app://app`)适配:HTTP 请求(上传 / 下载 / 预览路由 / 图片)保持页面同源的相对地址——NEXT 只给主框架的同源请求附渲染进程令牌,直连 `streamBaseUrl` 的 http 地址会被拒(`Browser access is disabled`);WebSocket 仍走 `streamBaseUrl`。下载在自定义协议页面上改为 fetch 取回再存盘(`` 是导航请求,拿不到令牌),失败时弹出原因 - 编辑器创建后自检隐藏输入框的定位,样式未生效时在控制台输出定位信息(样式标签数量 / 长度、是否 Shadow DOM、UA 等);补样式时改用编辑器所在文档 - 信任围栏:回环主机名视为同一族(`localhost` / `127.x` / `[::1]` 互访放行,含 `sec-fetch-site: cross-site`),外站与局域网规则不变 - 插件宿主端还是旧版本(升级后只刷新了页面)时,「添加工作区」不再显示 `transport failure ... HTTP 404`,改为提示完全重启 DSH - Windows:原子写的改名步骤遇到杀毒 / 索引占用(EPERM / EACCES / EBUSY)时退避重试约 0.6 秒,失败则清理临时文件;自动解锁的 DPAPI 调用改用 `%SystemRoot%` 下 PowerShell 的绝对路径,被系统策略(AppLocker / 受限语言模式)拦截时给出可读原因 - macOS:本机路径比较不区分大小写(APFS 默认不区分),与 Windows 一致 - 跨平台修正(macOS / Linux): - 远程工作区路径映射:占位目录在 macOS / Linux 上本身以 `/` 开头,原先会被当成远程路径原样发给远端(Agent 工具与侧栏打开文件都受影响);现在先判断是否位于占位目录下(宿主端同时比较字面路径与 realpath),客户端比较键只在 Windows 风格路径上忽略大小写 - 私钥路径支持 `~/.ssh/...`(原先 readFileSync 不认 `~`),中文占位提示同步改为 `~/.ssh/id_ed25519` - 终端:macOS 搜索改为 ⌘F(Ctrl+F 留给 readline / vim / less),Option 当 Meta(Alt+B/F/D 按词移动);Ctrl+Shift+C / V 补 `preventDefault`,修复 Chromium 上粘贴两次;右键菜单的快捷键提示按平台显示 - 保险箱、known-hosts、绑定、终端记录、偏好文件以 0600 写入、数据目录 0700 创建(macOS / Linux 原先是 0644 / 0755,明文字段对同机其他用户可读) - ssh-agent 认证在 Windows 上回退到 OpenSSH 命名管道;Windows 保留名判定补上带扩展名的情况(`aux.api` 等) - 「添加工作区」目录浏览的错误改为横幅样式:标题说明哪个目录/操作失败,不可读时给出可能原因,原始报错折叠在「详情」里(可选中复制),带「重试」与关闭;进不去的子目录不再影响当前列表 - Windows 卷根的系统保留目录(`System Volume Information`、`$RECYCLE.BIN` 等)按隐藏处理——部分磁盘 / 网盘挂载(如 115 网盘)不会给它们打隐藏属性,点进去会报 `EINVAL ... readdir` - 修复:115 网盘等挂载盘上的**空文件夹**打不开(报 `EINVAL: invalid argument, readdir`)。这类驱动对空目录的枚举直接返回失败(cmd `dir` 显示 File Not Found),现在遇到该错误时先列上一级确认目录存在,存在就按空文件夹显示,可以正常选作工作区 - 终端 WebSocket 地址改为优先使用宿主注入的 `__DSH_TRANSPORT__.streamBaseUrl`(与宿主自身流通道一致),不再只看页面地址——针对 macOS 桌面版终端一直「连接中断,正在重连」而 SFTP 正常的反馈;首次断开时在控制台输出目标地址,服务端拒绝终端连接的日志补上 Origin / Sec-Fetch-Site 便于排查 ## 0.10.0(2026-09-29) - 移动端/窄屏适配:新增 `useNarrow()`(主区按视口宽度判断)与 `@media (max-width: 640px)` 样式段——主机列表不再换行、改为整卡左右滑动;页签横向滚动;页面内边距与头部收紧(简介在窄屏隐藏) - 「上锁」与「更多」(改主密码 / 导出 / 导入)从头部移到「全局配置」页签新增的「保险箱」区,头部只留标题 - 远程文件在窄屏改为自适应单列;点文件后的预览/编辑改成全屏浮层(带关闭按钮,未保存修改仍会确认),不再占用右侧一列;浮层以本页为定位上下文(`absolute`,不再压住宿主网页版头部),并在窄屏隐藏编辑器顶部的「大小 · 权限 · 修改时间」一行 - 隐藏 Monaco 在 iPad / 触屏 UA 下自带的悬浮键盘按钮(`iPadShowKeyboard`,点了不会按预期唤出输入法) - 「新建/编辑主机」表单去掉「SSH Agent」认证方式(旧数据在表单里按「密码」展示,底层类型与存储保留;`vault/store.ts` 的四个 switch 顺带补 `default` 兜底),并修正窄屏表单单列排布 - 「添加工作区」弹窗:窄屏改单列并收紧高度预算(左栏限高、底栏竖排),避免输入框与按钮被挤出屏幕 - 接管「添加工作区」的优先级由 -200 降到 -1000,注册回调加 try/catch 与「head 是否为自己」自检——针对网页端被 dsh-bridge 抢占弹窗的问题(dsh-bridge 用 -10、dsh-remote 用 -100,宿主规则是 priority 最小者渲染) - 修复:公网访问时「添加工作区」仍弹出 dsh-bridge 的弹窗。根因有两条:① 动态包的 priority 会被宿主运行器改写成页内递减计数,-1000 不生效——改为「不是 head 就重注册」直到压过 dsh-bridge,并订阅插槽变化随时抢回;② dsh-bridge 在 document 捕获阶段按按钮文字截走点击(`stopImmediatePropagation`),宿主根本不会打开插槽——改为在更早的 window 捕获阶段临时中和按钮的 aria-label,事件照常交给宿主(仅在开启接管时生效,见 `workspace/bridge-compat.ts`) - 修复:移动端「添加工作区」进入盘符后目录列表无法滚动、只显示前几行(窄屏单列用的 grid + 自适应高度让列表拿不到有限高度)——改为固定高度的纵向 flex,目录列表独立滚动 - 修复:手机上点左侧「远程工作区」进入本页时,抽屉图标收成窄栏但抽屉容器仍然打开(装了 dsh-bridge 时抽屉容器由它的 `body.dsh-drawer-open` 控制)——收起时两边一起收 - 手机上从抽屉里点「添加工作区」打开弹窗后,自动收起抽屉(同样同时收宿主与 dsh-bridge 两侧) ## 0.9.1(2026-09-29) - 修复:浏览器 bundle 注册到 `window.__ModuleLoader__` 的 id 仍写死旧包名,宿主 `dsh-client-modules` 按包名核对不通过(`loaded without registering ""`),会让 DSH Desktop 直接启动失败(0.8.0 / 0.9.0 都受影响);现改为从 `package.json` 现读包名,并补冒烟断言 `ModuleLoader 注册 id = 包名` 兜住这类改名漏改 ## 0.9.0(2026-09-29) - 适配 DSH 0.2.0-rc.1 内核:`peerDependencies` 里 `@deepseek-ai/dsh-*` 的范围由 `^0.1.5-rc.2` 改为 `^0.1.5-rc.2 || ^0.2.0-rc.1`(旧内核仍可安装;0.2.x 内核不再被宿主判定为不兼容而禁用插件) - 开发依赖(`@deepseek-ai/*`)同步升到 0.2.0-rc.1,README 的依赖表注明同时兼容 0.1.x / 0.2.x ## 0.8.0(2026-09-29) - 包名由 `dsh-workspace` 改为 `@yh4922/dsh-workspace`(npm 上原名已被他人占用);`cordis.patch.yml` 里 insert 的 name 必须与包名一致(否则插件加载不到),远程清单的身份名 `PACKAGE` 也一并对齐 - 发布流程新增发布到 npm(`registry.npmjs.org`,`publishConfig.access=public`),并在 GitHub Release 额外上传固定文件名 `dsh-workspace.tgz` 供不带版本号的安装地址使用 - 安装说明改为不带版本号:主推 `dsh plugin add @yh4922/dsh-workspace`,补充升级方式与旧包名迁移说明 ## 0.7.7(2026-09-29) 首个公开发布版本。以下为自 0.7.2 以来的变化,更早的版本仅为本地开发版本。 - 远程工作区页面新增「全局配置」页签:接管「添加工作区」、自动解锁两个开关移到这里(标题栏只保留上锁 / 更多) - 「全局配置」里新增「关于」:版本、作者、许可证,以及仓库、更新日志、版本发布、使用说明、问题反馈的链接 - 修复:「接管添加工作区」开关被撑变形、挤出可视区,且开关旁没有文字说明 - 远程文件、远程 Git 在宽屏分栏时,左侧列表宽度可拖动调整:最小 240px、右侧至少保留 320px;双击恢复默认,按面板分别记住 - 远程文件 / 远程 Git 的复制提示改为浮窗,不再插在列表顶部造成抖动 - 添加工作区弹窗:远程主机按分组树形展示;选中主机显示「连接中」、不阻塞界面、失败可重试;「此电脑」「远程主机」可展开收起 - 修复:远程工作区页面被宽内容撑开 ## 0.7.2 - 远程文件树、Git「文件」页与改动列表加右键菜单(重命名 / 复制 / 粘贴 / 复制路径 / 暂存 / 放弃等) - 修复:Agent 运行时远程 Git 面板反复刷新、关掉右侧预览 ## 0.7.1 - 主机 / 分组弹窗加宽;分组表单精简为名称 + 父级分组;分组下拉支持筛选与新建 - 主机可拖到分组里换分组 ## 0.7.0 - 重做「添加工作区」弹窗:左侧本机快捷位置、盘符与远程主机,右侧目录浏览(上一级、面包屑、路径输入) - 接管开关即时生效,不用刷新 ## 0.6.x - 0.6.4:远程 Git 统一为 文件 / 改动 / 历史 三个页签 - 0.6.3:分支切换移到顶部下拉(筛选、刷新、一行式提示) - 0.6.2:可为每个会话设置默认查看的分支 - 0.6.1:远程文件 / 远程 Git 界面样式优化(统一图标、状态徽标、分叉图引用胶囊) - 0.6.0:保险箱「在本机记住」自动解锁(Windows 使用 DPAPI) ## 0.5.x - 0.5.4:改动页自动刷新;diff 可「查看源文件」 - 0.5.3:检出在另一个 git 工作目录的分支可直接修改与提交;修复会话顶部标签不出现 - 0.5.2:修复 diff 左右 / 上下切换、右侧内容不更新 - 0.5.1:查看分支时可编辑与提交(未检出分支经临时索引提交) - 0.5.0:远程文件 / 远程 Git 自适应分栏;会话顶部新增「远程文件」「远程 Git」标签 ## 0.4.x - 0.4.1:分支改为只读查看(不检出) - 0.4.0:远程 Git 面板:改动、diff、分支、带分叉图的历史 ## 0.3.x - 0.3.1:Markdown 预览;离线安装脚本自动关闭 DSH - 0.3.0:远程工作区右侧栏:远程文件查看编辑、远程文件树、SSH 终端;HTML 预览 ## 0.2.x - 0.2.1:修复远程会话的 write 写到了本机 - 0.2.0:远程工作区绑定、Agent 远程工具(read / write / edit / glob / grep / bash)、添加工作区弹窗 ## 0.1.x - 主机与分组管理、凭据保险箱、SSH 连接池(跳板、代理、指纹校验)、交互式终端、SFTP 文件浏览、Monaco 编辑器