Skip to content

Releases

v0.5.9 — 修桌面窗口重启 403 与重启后不自动重连

Choose a tag to compare

@k-ying k-ying released this 06 Nov 08:48

修 桌面窗口里点「重启」仍然 403,以及 重启后编辑器卡在 “Cannot reconnect. Please reload the window.”。

修复 1:桌面窗口里写操作仍然 403

  • 真因(0.5.8 新增的排查字段 fenceReject 记下来的):桌面壳把页面的请求用自己的代理转发时丢掉了 Origin 头,但保留了 Sec-Fetch-Site: none。0.5.8 只放行了「非 http(s) 来源」,对「根本没有来源」的写操作仍然拒绝
  • 修法:围栏接受「无 Origin + Sec-Fetch-Site: none + 回环 socket」的写操作。Sec-Fetch-Site 由浏览器生成、网页设置不了(网页只会产生 same-origin / cross-site);普通本地脚本两个头都不带,仍然拒绝

修复 2:重启后编辑器不自动恢复

  • 真因:随机端口在同一个插件实例里只生成一次,重启后 url 不变,而前端只凭 url 判断要不要重载 iframe → 永远停在旧页面,VSCode 一直显示 “Cannot reconnect”
  • 修法:/state 新增 bootAt(每次真正拉起 code-server 时刷新),前端据此重载 iframe。实测重启后 4 秒自动重连,并自动重放最后一次 diff

变更:声明宿主版本范围

  • package.json 增加 engines.dsh: ">=0.1.0-rc.1 <0.3.0-0"。此前只有一条 @deepseek-ai/dsh-client-runtime 的 peer,而它是客户端包、宿主安装目录里并不存在,dsh-market 的「宿主要求」只能显示「未声明宿主要求」;现在会显示 DSH >=0.1.0-rc.1 <0.3.0-0
  • engines.dsh 一旦声明就是独占判定(安装闸门与市场都不再回看 peerDependencies),所以范围必须自己写全;两侧的 satisfies 都带 includePrerelease,不会误杀 0.2.1-alpha.1 / 0.2.2-rc.1 这类预发布宿主
  • README 前置要求从 0.0.x – 0.2.x 收敛为 0.1.0-rc.1 – 0.2.x(中英同步)

测试

  • control-trust-smoke.mjs 81 → 84 项(新增 3 条桌面代理形状的用例,变异验证过)
  • npm test = 186 项

升级须知

  • 宿主半改动:装好后需要让新代码生效(市场热挂载会替换;重启 dsh 同样有效)
  • vscode-ext/dsh-bridge 未改动,扩展版本保持 0.5.3(major.minor 仍是 0.5)

English summary: fixes the Restart button still returning HTTP 403 in the desktop window, and the editor getting stuck on "Cannot reconnect. Please reload the window." after a restart. (1) The 403: the desktop shell proxies the page's requests natively and drops the Origin header while keeping Sec-Fetch-Site: none; 0.5.8 only accepted non-http(s) origins, so a request with no origin at all was still rejected. The fence now accepts a mutation with no Origin when Sec-Fetch-Site is none and the socket is loopback — a web page cannot set that header (browsers send same-origin/cross-site), and a plain local script sends neither header and is still rejected. (2) The reconnect loop: the random port is generated once per plugin instance, so a restart keeps the same URL; the client only reloaded the iframe when the URL changed, so it stayed on a dead page. /state now exposes bootAt, refreshed on every successful code-server start, and the client reloads the iframe when it changes — verified end-to-end: Restart returns {"ok":true} / HTTP 200, and the extension reconnects by itself about 4s later and replays the last diff. Also declares engines.dsh: ">=0.1.0-rc.1 <0.3.0-0" so dsh-market shows a host requirement instead of "undeclared" (it previously had only a @deepseek-ai/dsh-client-runtime peer, which is a client package absent from the host install); README requirements narrowed from 0.0.x to 0.1.0-rc.1. Tests: 81 → 84 in control-trust-smoke.mjs, mutation-verified; npm test = 186 checks.

v0.5.8 — 修桌面窗口里写操作一律 403

Choose a tag to compare

@k-ying k-ying released this 06 Nov 07:43

修 桌面窗口里所有写操作 403。

⚠️ 桌面版用户请升级:不升级的话,在桌面窗口里点「重启」、保存设置、切后端都会报
「操作被拒绝:HTTP 403 —— 写操作没通过控制面围栏」,而面板看上去一切正常。

修复(桌面窗口里写操作一律 403)

  • 现象:dsh 0.2.2 起,桌面窗口里点「重启」、保存设置、切后端,顶栏报「操作被拒绝(restart):HTTP 403 —— 写操作没通过控制面围栏……」,而面板本身能正常显示状态。只坏写操作,不坏只读
  • 根因:控制面围栏要求写操作的 Origin 与 Host 同源。桌面窗口的页面来自自定义协议(dsh-app://app),它永远不可能等于 http://127.0.0.1:3080 的 Host;而 new URL('dsh-app://app').origin 在 Node 里是 opaque 的 "null"。GET 不检查 Origin,所以只有写操作被拒 —— 看上去就像"设置保存坏了"
  • 修法:围栏接受任何非 http(s) 的页面来源,且仅在回环 socket 上放行。网页的 Origin 只可能是 http(s) 或 opaque 的 "null",所以自定义协议只可能来自应用窗口;"null"(file: / sandboxed iframe / data:)仍然一律拒绝——放行它才是真的 CSRF 洞。写成通用规则而非写死 dsh-app://app,壳将来换协议名(如 tauri://localhost)时不再复现
  • 排查抓手:围栏拒绝时把实际看到的 host / origin / sec-fetch-site / socket 地址记进 /state 的 fenceReject,下次 403 不用猜是哪条规则拦的

测试

  • test/control-trust-smoke.mjs 75 → 81 项,新增 6 条(自定义协议来源可写、被壳标成 cross-site 仍可写、另一应用协议来源也可写、非回环来源拒绝、null 仍拒绝、其他端口的 http 来源仍拒绝)
  • 已做变异验证:退回旧行为后其中 3 条立刻失败
  • npm test = 81 + 28 + 53 + 21 = 183 项

升级须知

  • 这是宿主半改动:装好后需让新代码生效(市场热挂载会替换;重启 dsh 同样有效)
  • vscode-ext/dsh-bridge 未改动,扩展版本保持 0.5.3(major.minor 仍是 0.5)
  • 浏览器访问(http://127.0.0.1:3080)本来就正常,此版对其无影响

English summary: fixes every write from the desktop shell window being rejected with HTTP 403 ("the write did not pass the control fence"). The control-plane fence required Origin to match Host, but the desktop window's page origin is a custom scheme (dsh-app://app), which can never equal http://127.0.0.1:3080 — and new URL('dsh-app://app').origin is the opaque "null" in Node. GETs never checked Origin, so only mutations broke, which looked like a broken settings save. The fence now accepts any non-http(s) page origin, on a loopback socket only: a web page's origin is always http(s) or the opaque "null", so a custom scheme can only come from an application webview. The opaque "null" (file:, sandboxed iframe, data:) is still rejected — allowing that would be the real CSRF hole. Written as a general rule rather than hard-coding dsh-app://app, so renaming the shell's scheme cannot bring the bug back. Rejected requests now record the observed host / origin / sec-fetch-site / socket in /state as fenceReject. Host-half change: let it reload after upgrading (the market hot-mount replaces it; restarting dsh works too). control-trust-smoke.mjs 75 → 81 checks, mutation-verified; npm test = 183 checks. Browser access over http://127.0.0.1:3080 was never affected.

v0.5.7 — 修 dsh 0.2.1-rc.4+ 上会话顶栏整条消失

Choose a tag to compare

@k-ying k-ying released this 01 Nov 16:06

修 dsh 0.2.1-rc.4 / rc.5 上「会话顶栏整条消失」。

⚠️ rc.4 / rc.5 用户务必升级到本版:不升级的话,装上本插件后顶栏(标题 / 按钮 / tab 行)会整条不见。

修复(dsh 0.2.1-rc.4+ 上会话顶栏整条消失)

  • 根因是「label 返回 React 元素」违反插槽契约:conversation.view 的 label 契约是 string | (() => string),而插件从 rc.3 时代起让它返回一个 <span>(用来画彩色状态点,并兼职 2.5 秒轮询)。dsh 0.2.1-rc.4 起核心给 tab 量宽改成 for (const glyph of label),元素不可迭代 → 抛 TypeError: label is not iterable → SlotErrorBoundary 把整条 conversation.session.header abdicate 掉,顶栏(标题 / 按钮 / tab 行)整条消失,只剩约 20px 空白带;刷新才短暂恢复,随后同一路径再崩。rc.3 没有这段代码,所以这是 rc.4 引入的回归,触发条件是本插件违约的 label
  • label 回归纯文本,每次投影重新求值(切界面语言不用重新注册)。删掉兼任轮询的 TabLabel 组件 —— 让装饰性元素承担关键职责,正是这次事故的形态;轮询改由 startTabPoller() 独立管理,不再依赖 tab 是否挂载
  • 状态点改由伪元素绘制:核心的 .tabLabel 是 display:block,行内伪元素必然被挤到下一行;而 .tab::after 已被核心占用画激活下划线,不能抢占。所以改用 ::before + 绝对定位画在按钮右外侧(.viewTabs 有 32px 间距),不参与布局、不换行、也不覆盖下划线
  • 打标不再依赖网络:每秒重试且先用默认色,dsh 重启后 1 秒内出现;找不到按钮就静默降级为纯文字,绝不抛错
  • 留下排查抓手:<html data-dsh-vsced-diag> 记录插件实际看到的 tab 文本与匹配结果(只在值变化时写,平时零开销)

测试

  • test/client-slots-sim.mjs 新增「view label 必须解析为字符串」(25 → 28 项),已做变异验证:把 label 改回数组包元素,该断言立刻失败
  • npm test = 75 + 28 + 53 + 21 = 177 项

升级须知

  • 插件版本 0.5.6 → 0.5.7;vscode-ext/dsh-bridge 未改动,扩展版本保持 0.5.3(major.minor 仍是 0.5)
  • 这是客户端改动:装上后刷新页面即生效,不需要重启 dsh(重启也无害)
  • dsh 0.2.1-rc.4 / rc.5 用户务必升级到本版,否则顶栏会因上述回归整条消失
  • 上游 labelNeedsTooltip 缺少类型守卫的问题仍建议核心侧修复(第三方插件的非字符串 label 不该能吞掉整条顶栏);本版不依赖该修复,也未附带任何修改 dsh 核心的补丁

English summary: fixes the conversation header disappearing entirely on dsh 0.2.1-rc.4 / rc.5. The plugin had been returning a React element as the conversation.view tab label, while the slot contract is string | (() => string); rc.4 began measuring labels with for (const glyph of label), so a non-iterable element threw TypeError: label is not iterable and the slot error boundary abdicated the whole conversation.session.header (title, actions, tab row), leaving a ~20px blank strip. The label is plain text again (re-evaluated on each projection, so switching UI language needs no re-registration); the status dot is drawn by a ::before pseudo-element outside the button, so it never participates in layout or competes with the core's ::after active underline; polling moved out of the render path into startTabPoller() and no longer depends on the tab being mounted. <html data-dsh-vsced-diag> records what the plugin actually saw. Regression-tested and mutation-verified (npm test = 177 checks). Client-only: refresh the page, no dsh restart required.

v0.5.6 — 修 dsh 0.2.x 上的设置持久化

Choose a tag to compare

@k-ying k-ying released this 08 Oct 06:58

dsh-vsceditor 0.5.6 — 修 dsh 0.2.x 上的设置持久化

⚠️ 装到 dsh 0.2.x(含桌面壳自带的 0.2.1-alpha.1)后,设置改不动、重启就丢的用户,本版就是修复。 0.5.5 及更早会显示「⚠️ 设置服务不可用,以上改动仅本次运行有效 / 设置命名空间注册失败:sctx.settings.register is not a function」。

升级说明:插件版本 0.5.5 → 0.5.6。扩展版本保持 0.5.3(本版未改动 vscode-ext/dsh-bridge,major.minor 仍一致)。装上后需要重启 dsh —— 本版改的是宿主半(lib/host.js)的代码,不重启仍会跑旧逻辑。

修复一:0.2.x 上设置不持久化

dsh 0.2.x 的 settings 服务删掉了 register()(0.1.5 有,返回带 get/watch/update 的 scope)。插件原先直接调它,抛错被 catch 后 settingsScope 为空,于是:

  • writeConfig() 提前返回,改动只写进内存
  • /state 报 settingsAvailable: false,面板顶出那句警告

现在按能力分流:settings.register 存在 → 走 0.1.x 老路;否则 → 写插件自己的 profile 条目:

configEditor.edit(entry, change)   // 校验 → 落盘 profile patch → reconcile(patchReload: live)

写盘前先过一遍插件自己的 configSchema,坏值必须被拒绝,而不是落进用户的配置文件。

为什么不导出 Config? 0.2.x 的 settings 服务只投影 entry 的 Config schema,而且只接受带 meta.volatile 的字段(volatileForm())。本插件是零依赖、手搓 schemastery 兼容外形,没有 meta.volatile;更重要的是 cordis 会拿 runtime.Config 跑 resolveConfig()、写回时还调 Config.simplify —— 手搓 schema 未必满足,最坏结果是插件整个加载失败。configEditor 走的是组合后的 row,resolveConfig 对没有 Config 的插件是直通,所以这条路既够用又安全。

修复二:部署层 trustedHosts 从未生效(与版本无关的既有 bug)

插件一直从 connection 服务读部署层的 trustedHosts,但该字段在 webRuntime 上(dsh-web-app 用 ctx.provide("webRuntime", { lanAddresses, trustedHosts }) 注册,0.1.5 与 0.2.1 都是)。外面套着 try/catch + Array.isArray 防御,所以失败是静默的 —— CHANGELOG 里 0.5.2 那条「自动并集 DSH 部署层 trustedHosts」从写下那天起就没生效过。现在改从 webRuntime 读;插件自身的 trustedHosts 设置不受影响。

设置写到哪去了(0.2.x)

写进该 profile 的 cordis.patch.yml:在既有 - id: vsceditor 行上更新 config:,没有就追加一条。顶层同 id 行是对组合层那一行的覆盖,不会重复挂载插件。

测试

  • test/control-trust-smoke.mjs 新增「dsh-generation adapters」13 项(62 → 75):deploymentTrustedHosts 必须读 webRuntime、不得读 connection(反向断言)、非数组/抛错/缺 ctx 都不炸;pickOwnEntry 按模块名精确匹配、退回比较 id 最后一段、不误认领 vsceditor-fork 这类无关行
  • 变异验证(均逐字节还原):退回读 connection 报 2 条失败;退回子串匹配报 1 条失败
  • npm test = 75 + 25 + 53 + 21 = 174 项

本版是怎么验证的

在一个临时 profile(~/.dsh/profiles/scratch,端口 3099,editorBackend: off,用壳自带的 dsh 0.2.1-alpha.1 启动)上实测,没有触碰正在使用的 tauri profile,用完即删:

检验 结果
/state settingsAvailable false → true,lastError 清空
POST /action {set-config} {"ok":true, …, "persisted":true}
落盘 真的写进 cordis.patch.yml(更新已有 row、没有 row 时追加 —— 两条路径都实测)
顶层同 id row 的语义 --dump-config 组合后仍只有 1 条 id: vsceditor → 覆盖而非重复挂载
部署层 trustedHosts 声明过的 Host 403 → 200,未声明的仍 403

English summary: fixes settings persistence on dsh 0.2.x, where settings.register was removed — the plugin threw sctx.settings.register is not a function, swallowed it, and every settings change lived only in memory (the panel showed "settings service unavailable, changes apply to this run only"). The plugin now branches on capability: 0.1.x keeps the namespace scope, while 0.2.x persists through its own profile entry via configEditor.edit(entry, change) (validate → write the profile patch → reconcile), validating with the plugin's own schema first so a bad value is rejected instead of landing in the user's config file. It deliberately does not export Config: cordis runs resolveConfig() over runtime.Config and calls Config.simplify on write-back, and the hand-rolled schemastery-compatible shape may not satisfy that — the failure mode would be the plugin not loading at all. Also fixes a version-independent latent bug: the deployment's trustedHosts were read from connection, where the field has never existed (it lives on webRuntime), so the advertised "union with DSH's own trusted hosts" had silently done nothing since 0.5.2. Verified end-to-end on a throwaway profile running the shell's bundled dsh 0.2.1-alpha.1 (settings now report settingsAvailable: true, a write persists into cordis.patch.yml, and a deployment-declared Host flips the control route from 403 to 200), with mutation-verified regression tests. 174 checks total. Restart dsh after upgrading.


⚠️ 补记(2026-10-08):从 0.1.x 升级上来的话,老设置不会自动迁移

0.1.x 时代设置存在 ~/.dsh/settings.yaml 的 dsh-vsceditor 段里。dsh 0.2.x 的那次一次性迁移是按段名匹配插件条目的,而我们的段名(dsh-vsceditor)与条目 id(vsceditor)对不上,因此这些值不会被搬过来,插件会以默认设置启动。

文件并没有丢 —— dsh 只是把它改名成了 ~/.dsh/settings.yaml.imported。两种补法任选:

  1. 在「设置 → 插件 → 内嵌 VSCode 编辑器」里重选一遍(字段不多,最快);
  2. 或把 .imported 里 dsh-vsceditor: 段的内容抄进该 profile 的 cordis.patch.yml:
- id: vsceditor
  name: dsh-vsceditor
  config:
    editorBackend: local
    vscodePath: /Applications/Visual Studio Code.app

然后重启 dsh。详见 README 的「5.5 设置卡片」一节。

这与本版修复的「0.2.x 上设置不持久化」是两个独立问题:本版修的是新版存不进,这一节说的是老版的值搬不过来。

v0.5.5 — 修 dsh 0.1.x 上设置界面被渲染两遍

Choose a tag to compare

@k-ying k-ying released this 08 Oct 06:17

dsh-vsceditor 0.5.5 — 修 0.1.x 上设置界面被渲染两遍

⚠️ 在 dsh 0.1.x 上装过 0.5.4 的用户建议升级:0.5.4 会让「设置 → 插件」里同时出现一个「内嵌 VSCode 编辑器」标签页和「插件配置」里的同一张卡片 —— 同一份设置渲染两遍。功能都正常,只是重复、碍眼。

升级说明:插件版本 0.5.4 → 0.5.5。扩展版本保持 0.5.3 —— 本版未改动 vscode-ext/dsh-bridge,major.minor 仍一致,升级不会触发扩展重拷,也不需要 Reload Window。

修复(0.1.x 上设置界面重复)

0.5.4 为兼容 dsh 0.2.x,把设置界面同时注册进了 settings.plugin.item 与 settings.plugins.tab,前提是「两代壳各只声明一个、各命中一条」。这个前提是错的 —— 0.1.x 的真实插槽拓扑是:

settings.section (id: plugins)
 └── children: settings.plugins.tab              ← 0.1.5 起就有
       └── 内建标签页 "configurable" (order 0)
             └── children: settings.plugin.item  ← keyed 卡片

两个插槽都被声明,所以两条注册同时命中。现在改为标签页优先:settings.plugin.item 仅作为「连标签页插槽都没有的更老壳」的兜底,并且标签页一旦出现就把已注册的卡片撤回 —— 无论声明顺序如何,最终恰好一份设置界面。

(slots.inject 对未声明的插槽只是挂起等待、不抛错 —— 0.1.5 与 0.2.1 的实现都是 specDynamic(key) === undefined → return,所以两条 inject 在任何壳上都是安全的。)

设置入口在哪

dsh 位置
0.1.5 及更新 设置 → 插件 → 「内嵌 VSCode 编辑器」标签页
更老(还没有该插槽的壳) 设置 → 插件 → 插件配置 → 「内嵌 VSCode 编辑器」卡片(兜底,点标题展开)

两处是同一份设置界面,字段完全一致。

测试

  • 新增 test/client-slots-sim.mjs(25 项):stub 模块加载器 + 桩化 DOM + 一个实现了真实 slots.inject 生命周期的假 slots 服务(声明即执行 / 塌缩即释放 / 重新声明再次执行),加载未修改的真实 lib/client.js,按六种声明形态断言「恰好注册一次」—— 含声明顺序颠倒(卡片先到、标签页后到必须撤回卡片)
  • 变异验证(三处,均逐字节还原核对):退回「两条都无条件注册」报 3 条失败;只去掉「撤回」报 2 条失败;去掉兜底卡片报 4 条失败
  • npm test 现为 62 + 25 + 53 + 21 = 161 项

已知限制

「0.1.5 的浏览器里真的只渲染一份」这件事是靠桩验证的(标准同 windows-sim.mjs 用桩跑 win32 语义),没有在真实 0.1.5 壳里端到端跑过。若你仍看到重复,请开 issue 并附上浏览器控制台截图。

English summary: fixes a 0.5.4 regression on dsh 0.1.x, where the settings surface was registered into both settings.plugin.item and settings.plugins.tab on the assumption that each shell generation declares only one. 0.1.x declares both — its settings.plugins.tab contains a shipped "configurable" tab whose children include settings.plugin.item — so the same settings UI rendered twice. The plugin now prefers the tab and treats the card purely as a fallback for older shells that lack the tab slot, retracting an already-registered card as soon as the tab appears, so exactly one surface remains whatever the declaration order. Adds a dependency-free simulation suite (25 checks) that drives the real lib/client.js against a stubbed module loader, DOM and slot service (mutation-verified), and corrects the README's description of where the settings live. 161 checks total.

v0.5.4 — 兼容 dsh 0.2.x:放开 peer 范围 + 迁移设置插槽

Choose a tag to compare

@k-ying k-ying released this 08 Oct 04:05

dsh-vsceditor 0.5.4 — 兼容 dsh 0.2.x

⚠️ 在 dsh 0.2.x(含 Tauri 桌面壳自带的 0.2.1-alpha.1)上装不上的用户,本版就是修复。 0.5.3 及更早会被 dsh 的兼容性闸门直接拒绝安装;本版放开范围,并把设置入口迁移到 0.2 起的新插槽。

升级说明:插件版本 0.5.3 → 0.5.4。扩展版本保持 0.5.3 —— 本版未改动 vscode-ext/dsh-bridge,major.minor 仍一致,因此升级插件不会触发扩展重拷,也不需要 Reload Window。设置字段与行为完全不变,只是入口位置随壳版本不同(见下表)。

修复(dsh 0.2.x 上装不上、设置界面不显示)

  • peer 范围挡住了整个 dsh 0.2.x:peerDependencies 原先上界停在 <0.2.0-0。dsh 装载时会拿每一条 @deepseek-ai/dsh-* 的 peer 声明与当前运行时版本做 semver.satisfies(runtimeVersion, range, { includePrerelease: true })(见 dsh-app-boot),不在范围内就直接拒绝安装,只留一条 exact-version 豁免的指引 —— 也就是你看到的那条 Plugin dsh-vsceditor@0.5.3 is incompatible with dsh 0.2.1-alpha.1。现在追加 >=0.2.0-0 <0.3.0-0:0.0.x/0.1.x 与 0.2.x 都能装,0.3.x 仍被拦住
  • 设置界面挂在了一个已消失的插槽上:0.2.1 起 settings.plugin.item 不再声明,第三方设置改为「设置 → 插件」下的标签页 settings.plugins.tab(dsh-market 自己也注册在那里)。原先的设置卡片只挂在旧插槽上,在新壳里既不报错也不显示。现在两条注册并存:旧插槽渲染可折叠卡片,新插槽渲染常显的标签面板(静态头部、无折叠按钮)
  • 核对结论(对着 0.2.1-alpha.1 的活体注册表,不是读文档推测):宿主侧契约仍然完好 —— 注入的 webServer / subprocess / timer / agents 四个服务、监听的 tools/pre-execute / tools/result / agent/created / agent/inbox/claimed 四个事件、ToolExecutionInput 的 name / arguments / agent 与 ToolExecutionResult.isError、客户端插槽 conversation.view 全部还在且形状一致。所以这是版本范围 + 一处插槽名的兼容问题,不是协议断代

设置入口在哪

dsh 位置
0.1.x 及更早 设置 → 插件 → 插件配置 → 「内嵌 VSCode 编辑器」卡片(默认折叠,点标题展开)
0.2.x 起 设置 → 插件 → 「内嵌 VSCode 编辑器」标签页(面板常显)

两处是同一份设置界面,字段完全一致。

测试

  • test/control-trust-smoke.mjs 新增一组「manifest 不变量」9 项(53 → 62):用最小实现的 semver 精度比较(语义等价于 dsh 的 includePrerelease,从而保持零依赖)断言 peer 范围接受 0.1.5-rc.1 / 0.2.0-0 / 0.2.0-rc.2 / 0.2.1-alpha.1 / 0.2.99、拒绝 0.3.0-0 / 0.3.0,并断言客户端同时保留新旧两条设置插槽注册
  • 变异验证:把 peer 范围退回旧值报 4 条失败,拿掉 0.2+ 插槽注册报 1 条失败
  • npm test 现为 62 + 53 + 21 = 136 项

已知限制

本版尚未在真实 0.2.1 环境里实际安装过:插槽存在性、注册参数、宿主服务与事件都是对着运行中壳的活体注册表核对的,但「标签页真的渲染出来」以及 dsh.client.inject 的模块解析,需要实际装一次才能确认。装完若有异常请开 issue,并附上 ~/.dsh-editor/bridge-ext.log。

English summary: fixes installation on dsh 0.2.x, where dsh's compatibility gate rejected 0.5.3 outright — the @deepseek-ai/dsh-client-runtime peer range stopped at <0.2.0-0 while the Tauri desktop shell ships dsh 0.2.1-alpha.1 (dsh-app-boot evaluates each @deepseek-ai/dsh-* peer with semver.satisfies(runtimeVersion, range, { includePrerelease: true })). Also migrates the settings surface: 0.2.1 dropped the settings.plugin.item slot, so the plugin now registers both it and the new settings.plugins.tab tab slot — rendering a collapsible card in the old shell and an always-open panel in the new one. The host contract was re-checked against the live 0.2.1-alpha.1 registry (services, events, ToolExecutionInput/ToolExecutionResult fields and the conversation.view slot are all unchanged). Adds 9 mutation-verified manifest invariants to the smoke suite, now 136 checks. Not yet installed on a real 0.2.1 runtime.

v0.5.3 — Windows 桌面模式路径修复(#4 / #5 / #6)

Choose a tag to compare

@k-ying k-ying released this 24 Sep 16:26

dsh-vsceditor 0.5.3 — Windows 桌面模式路径修复

⚠️ Windows + 桌面 VS Code(editorBackend: local)用户建议升级:本版修复三个报告(#4 / #5 / #6),其中 #4 会让扩展完全连不上(状态栏「工作区不匹配」,跟随 / diff / 锁定全部失效),#5 / #6 只有在 #4 修好后才观察得到。

升级说明:扩展版本 0.5.0 → 0.5.3。升级插件后 host 会自动把扩展重拷到 ~/.vscode/extensions/dsh.dsh-bridge 并提示 Reload Window。没弹提示的话,直接看 ~/.vscode/extensions/dsh.dsh-bridge/package.json 是不是 0.5.3,以及该目录下有没有 paths.js(本版新增文件,最直观的判据)。判定与排障流程见 docs/windows-verification.md。

修复(Windows 桌面模式路径处理,#4 / #5 / #6)

三条报告同源:路径在整条链路上被当成不透明字符串。修法是引入 vscode-ext/dsh-bridge/paths.js(纯函数、平台可注入),让扩展与 host 都按文件系统语义处理路径。

  • #4 工作区匹配忽略盘符大小写(@NightingaleCourt):workspaceMatches() 原先用 === 比较 workspaceFolders[0].uri.fsPath 与 bridge 里的工作区路径。真机实测:uri.fsPath 报的是小写盘符 c:\...,而 DSH 侧 bridge.json 是大写 C:\... —— 两者被判成不同目录,扩展永不连接。现在改用 sameFsPath()(win32 大小写不敏感 + 归一化分隔符/尾分隔符,POSIX 仍区分大小写)
  • #5 相对路径丢盘符:Agent 给出的工作区相对路径(Tdata\config.lua)被原样透传,扩展再 vscode.Uri.file() 就得到 \Tdata\xxx.lua。现在 host 侧 resolveEditPath() 先按会话工作区解析成绝对路径,扩展侧做防御性二次解析,lock/unlock/hello/edit/reveal 的路径统一规范化
  • #6 跟随 diff 右侧吃到 VS Code 文档缓存:右侧原先用 vscode.Uri.file(fsPath),文件已作为普通标签打开时左右两侧都显示改前内容。现在右侧改为虚拟文档 dsh-now://(内容取自 host 回读的磁盘内容),左侧 dsh-snap://,两侧都由扩展提供,彻底绕开 TextDocument 缓存 —— 这个改法就是报告者在 issue 里建议的方案
  • 同族另修三处:encodePath() / diff 标题的 baseName() 原先按 split('/') 切分(win32 反斜杠路径会坍缩成一整段)、followWorkspaceOnly 的包含判断按 root + path.sep 前缀比较(盘符大小写不同时会把工作区内的文件误判为区外并静默不弹 diff)

修复(平台无关,真机验证时顺带发现)

两条都在 0.5.2 及更早就存在、且与 Windows 无关,是跑帧级日志时暴露的:

  • SSE 每 2.5s 重连的自维持循环:connectSSE() 顶掉一条活流时,被顶替的流会以 error: aborted 走完自己的 error 回调,原代码没有门禁 → 该回调排下一次重连 → 2.5s 后又顶掉当时活着的流,永不停止(状态栏闪烁、日志刷屏、destroy/reconnect 瞬间可能丢帧)。触发场景很常见(activate() 后紧跟信任授予 / 工作区变更)。现在所有 handler 先判 state.sseReq === req
  • 编辑帧去重键碰撞:去重键是 path|长度|抽样哈希,抽样步长 i += 97 对短于 97 字符的正文只跑一次、哈希退化成首字符码 → 「等长 + 同首字符」的不同编辑被判成同一份帧:不再聚焦、不再定位改动行,标签被关掉后 30s 内不重开。现在精确比较上一帧的 (路径, 正文)

改进

  • 重连退避:已经拿到端点却握不上手 / 被断流时按 2.5s → 30s 指数退避(此前固定 2.5s,token 不同步期间本机日志里留下了连续 27 次 SSE 握手失败:HTTP 403)。发现阶段(bridge.json 尚未出现、工作区不匹配)保持固定 2.5s —— 这两种只能靠轮询发现,退避会让刚切到本机模式的人白等半分钟
  • CI 加平台矩阵:.github/workflows/test.yml 从单个 ubuntu-latest 改为 ubuntu-latest / windows-latest / macos-latest。#4/#5/#6 恰好是 CI 看不见的类型(模拟用例本来就是注入 win32 的,只有真实 runner 才跑到默认 process.platform 分支)。矩阵第一次运行就抓到一个测试自身的时序假设过紧(Windows 上第二次连接需 ~200ms,用例只等 80ms),已修

文档 / 测试

  • 新增 Windows 真机验证报告(docs/windows-verification-report.md):三条修复在 Windows 10 Pro 22H2 + VS Code 1.138.0 上全部通过,含帧级日志、人眼确认项(diff 标题与右侧内容)与证据强度评估
  • 新增 docs/evidence/probes/:4 个复现脚本,其中两个平台无关可直接跑(sse-loop-repro.mjs 已在 Windows 与 macOS 双平台复现 2.5s 循环)
  • 新增 test/bridge-regressions.mjs(21 项):用真实 extension.js + 桩化 vscode + 真实 HTTP SSE 服务端驱动缺陷 A/B。此前这两处无法被测试覆盖(connectSSE() 未导出、去重键由未导出的内部函数计算)。已做变异验证:把两处修复退回旧行为后该用例报 8 条失败,其中一条正是循环复现
  • npm test 现为 53 + 53 + 21 = 127 项

English summary: fixes the three Windows desktop-mode path reports (#4/#5/#6) by making paths filesystem-aware via a new pure-function paths.js (drive-letter case, separator normalization, workspace-relative resolution, and a virtual dsh-now:// document for the diff's right side so VS Code's TextDocument cache is bypassed). Also fixes two long-standing platform-independent defects found while verifying on a real Windows machine: a self-sustaining 2.5s SSE reconnect loop (a superseded stream's aborted error scheduled the next reconnect) and an edit-frame dedup key that collapsed to path|length|first-char for anything shorter than 97 characters. Adds reconnect backoff for known-endpoint failures, a native three-OS CI matrix, and a regression suite driven against a real HTTP SSE server (mutation-verified).

v0.5.2 — 控制面围栏加固(安全修复)

Choose a tag to compare

@k-ying k-ying released this 20 Sep 15:20

dsh-vsceditor 0.5.2 — 安全修复

⚠️ 建议所有 ≤0.5.1 的用户升级:本版修复控制面接口可被任意网页驱动的漏洞。

安全

  • 控制面路由加信任围栏(感谢 @sheecegardezi 的 PR #3):/state 与 /action 此前没有任何校验 —— 任意网页可跨站 POST set-config 写入攻击者选定的 vscodePath,再 detect-vscode 让宿主执行该二进制(CSRF → 命令执行)。该路径真实可达:readBody 不看 content-type,用默认 text/plain 的跨站 fetch 属简单请求、不触发预检;DNS rebinding 下还可直接读取 /state(泄露工作区与最近文件路径)
  • 非回环 socket 只能代表已声明的信任主机:浏览器之外 Host 与 Origin 都可以伪造,socket 地址是唯一可信信号。没有这条规则,本机任意进程(或 DSH 绑到局域网时的任意局域网主机)只要声称 Host: 127.0.0.1 即可绕过围栏;同机经本机局域网地址/主机名访问仍然放行
  • 新增 trustedHosts 设置:逗号分隔的裸授权(host 或 host:port),与 DSH 核心同语义,供反向代理 / 自定义域名 / Tailscale MagicDNS / ngrok 等场景声明信任主机;自动并集部署层 connection.trustedHosts,不必重复声明
  • set-config 键白名单;code-server 随机端口扩到完整 10000–65000 段;bridge.json 改 0600 权限
  • 残余风险(如实记录):内嵌 code-server 仍为 --auth none(仅回环),多用户机器上本机其它用户仍可能经端口扫描访问——建议改用 local 后端

修复

  • IPv6 误判:new URL('http://[::ffff:127.0.0.1]:3080') 会把 hostname 规范成带方括号的 [::ffff:7f00:1],原正则匹配不到,导致非回环 IPv6 访问被 403
  • 端口范围文案同步:lib/host.js 4 语言 cfg.port、客户端 settings.portHint、两个 README 设置表原先仍写 18200–18900

文档 / 测试

  • README 安全内容并入既有的「安全说明 / Security notes」小节(原先被插成无编号的 ## Security,打乱章节编号),并同步 README.zh.md
  • 新增排障条目:经反向代理 / 自定义域名访问时控制接口 403、设置卡片自身也存不了的处置方式
  • 围栏矩阵扩测(socket 来源校验、信任主机端口语义、IPv4-mapped IPv6、trustedHosts 解析),新增 GitHub Actions 工作流跑 npm test

English summary: fences the control-plane routes (CSRF / DNS-rebinding), and hardens the fence beyond browsers: a non-loopback socket may only speak for a host declared in the new trustedHosts setting (which also inherits DSH's own connection.trustedHosts), because Host and Origin are both forgeable outside a browser. Fixes an IPv6 false-403, syncs the random-port range text, folds the README security notes into the existing section (plus zh mirror), and extends the test suite with CI.

v0.5.1 — 编辑器后端三态互斥:新增「关闭」模式

Choose a tag to compare

@k-ying k-ying released this 09 Sep 02:35

dsh-vsceditor 0.5.1

新增

  • 编辑器后端新增「关闭」互斥项:editorBackend 从 embedded/local 二选扩展为 embedded / local / off 三态互斥(设置 → 插件 → 插件配置 → 编辑器后端,单选按钮)。选中「关闭」后不连接任何后端:host 停掉看门狗(级联关闭整棵 code-server 进程树,零孤儿)、摘除 bridge.json 让本机 VS Code 扩展断开,释放内存;关闭态下自动启动、崩溃重试、重启定时器、安装完成拉起等所有拉起路径全部被 startServer 的 off 守卫拦截,不会重复启动。关闭状态持久化,DSH 重启后保持关闭
  • 编辑器工具栏电源按钮:重启按钮右边新增「关闭 code-server」(本机模式为「断开连接」),与设置卡片的关闭项联动同一配置位;关闭态下该按钮变为「打开 code-server」
  • 关闭态引导界面:编辑器标签页在关闭态显示按需启动引导——「启动内嵌 code-server」或「连接本机 VS Code」一键切换;状态灯为灰色(已关闭);常驻 iframe 同步释放渲染进程内存

English summary: editorBackend gains a third mutually-exclusive option Off (Settings → Plugin Configuration, or the new power button in the editor toolbar). Off stops the watchdog (SIGTERM to the whole code-server tree, no orphans), unlinks bridge.json for local mode, releases the resident iframe's renderer memory, and gates every relaunch path (auto-start, crash-retry, timers, install-finish, workspace switch) behind a single guard. Persisted across DSH restarts; turning back on from Off always starts immediately regardless of autoStart.

v0.5.0 — 轮次级 diff + code-server 孤儿治理

Choose a tag to compare

@k-ying k-ying released this 08 Sep 02:47

新增

  • 轮次级 diff 生命周期:diff 基线跟随对话轮次而非标签页。一轮内改动按文件累计;关掉 diff 标签不丢状态(再编辑/点文件/刷新编辑器都会带原基线恢复);下一轮对话产生首次编辑时上一轮 diff 才整体清场
  • code-server 看门狗:心跳发现 DSH 死亡后自动杀整棵 code-server 进程树,不再残留孤儿进程(此前一次升级能留下十几个)
  • 孤儿收割器:启动 + 30s + 每 30min 巡检回收历史残留;不误伤本机 VS Code 或自装 code-server

修复

  • 崩溃重试计数改为稳定运行 30s 后重置,长期运行后编辑器不再因额度耗尽而无法自动拉起

完整变更见 CHANGELOG。