github zhu1090093659/dsh-web v0.4.8
dsh-web v0.4.8

4 hours ago

本次发布把聚合包的家族行改为直挂真实包,兼容层不再随聚合包下发,聚合客户端 bundle 从 2.9 MB 缩到 22 KB;远程桥接成为聚合包的一行,并沿协议 v1 继续扩展分叉、增量名册、帧压缩、后台任务与子代理线程;新增 dsh-task-board-remote,把任务看板带上远程面;设置页给出全家桶遥测开关,技能中心支持筛选、折叠与导入,任务看板增加每日令牌上限与执行结算记录,SSH 支持按主机开启代理转发,用量卡标出 DeepSeek 峰谷时段。插件管理器在这一版补齐了三处更新链路的边界:失效行名修复、被替换 bundle 的层序恢复,以及「无法判定」与「DSH 版本过低」的区分。

会真正迁移的入口只有一个:经插件管理器更新(含聚合包旧名的迁移分支)会做预检、版本断言与回滚,它是唯一具备这三件事的路径。本版本仍然没有 Doctor 启动前迁移——dsh-doctor 当前只是 tombstone,legacy-migration.ts 里提到的启动前置消费者在本版本并不存在;直接用 dsh web 启动宿主不执行任何安装或预检,旧 profile 会一直保持原样。请经插件管理器完成更新,需要手工处理时用 dsh plugin --profile <name> add @linxin666/dsh-web-all@0.4.8。dsh-update 家族插件是第四条路径:它搬动版本并校验结果,但没有备份、没有预检、没有回滚,也不修行名。
行名重写会在启动时自动补齐:0.4.8 的家族行以真实包名挂载(例如 @linxin666/dsh-client-ui-task-board,而不是 @linxin666/dsh-web-all/task-board)。仍带旧名的 profile 行在这一次启动里被整行跳过,用户此前保存的 disabled 与 config 因此不生效——被关掉的插件可能重新挂上。插件管理器在宿主启动稳定 1.5 秒后改写行名,同一处修复也在更新任务里执行;写入前先备份到 cordis.patch.yml.bak-plugin-manager,只在确有差异时写,可重复执行。运行中的宿主是否即时重读这份文件没有保证,重启一次即可完成修复;磁盘上不会丢数据,也不会多出一行。
残留告警:只有 profile 的 bundle 声明可解析时,插件管理器才会自动清理已退场的 web-ui-model-capabilities 覆盖行;CLI 建出的 profile 只在 dsh.profile.bundles 里声明 @deepseek-ai/dsh-base 与 @deepseek-ai/dsh-web-app,并不安装它们,判定因此为「无法判定」,清理逻辑不删任何东西。所以这类 profile 在每一条升级路径上(包括经插件管理器更新)都会留下该覆盖行:组合结果本身仍然正确,唯一症状是每次 dsh --dump-config 与 CLI 日志里都会重复一条 patch: entry "web-ui-model-capabilities" not found。手工处理:先备份,再编辑 $DSH_HOME/profiles/<profile>/cordis.patch.yml,删掉 id 为 web-ui-model-capabilities 的那一段。
@linxin666/dsh-client-ui-model-capabilities 继续处于退场状态,没有替代包:自定义模型的输入类型与推理档位由官方 Models 设置页编辑。它的 cordis 行不再让组合失败,而是挂载空壳;退场插件名册由 aggregate.yml 的 retiredPlugins: 生成(本版改为从清单生成,不再手写),命中的说明符在加载前被跳过,并在插件清单里给出一条 “Removed ...” 说明。
远程桥接现在是聚合包的一行(web-ui-remote-bridge):装载聚合包的 profile 升级后即带一个协议 v1 监听器,监听 /ios/v1,另有一个只对来自本机的请求开放的配对令牌端点 /remote-bridge/pair/token,无需再单独安装。它与独立安装的 @linxin666/dsh-remote-bridge 互斥单实例(mountOnce),不会双注册路由。
故障单元变大了:家族行直挂真实包之后,某个家族插件导入或启动失败不再被容错外壳隔离成「一行降级」,而是让整个 loader 组回滚。这是有意的取舍(见 issue #1901 的决定记录),原先由宿主掌握的启动审计行为不变。
pnpm 供应链限制:pnpm 配置若启用 minimumReleaseAge(常见默认 24 小时),在包发布后不久执行的更新会被挡住。补救办法是在 profile 的 pnpm-workspace.yaml 里为 @linxin666/* 补上 minimumReleaseAgeExclude。
卫星范围在同一批次里对齐:四个卫星仓先各自发布 0.4.8,registry 可解析之后,聚合包的依赖范围才从 ^0.4.7 抬到 ^0.4.8:

  • 皮肤中心(dsh-skins):@linxin666/dsh-client-ui-skin-center@0.4.8
  • 宠物(dsh-pet):@linxin666/dsh-pet@0.4.8
  • 社区插件索引(dsh-community-plugins):@linxin666/dsh-client-ui-community-plugins@0.4.8
  • 预设中心(dsh-presets):@linxin666/dsh-client-ui-preset-center@0.4.8

新功能

  • [remote-bridge] 新增通用协议扩展缝:插件可以应答 KNOWN_METHODS 之外的方法,客户端声明该插件要求的能力后即可调用
  • [remote-bridge] 问答请求把宿主侧截止时刻作为 expiresAt 下发
  • [remote-bridge] session.fork 落地并带 session.fork.v1 能力,手机可从一个会话分叉出新的会话
  • [remote-bridge] 新增 agent 通道,覆盖预设枚举与会话创建
  • [remote-bridge] 名册与看板支持增量应答,session.list 与 board.snapshot 按 since 令牌给出重置或增量
  • [remote-bridge] 名册不再携带已归档会话
  • [remote-bridge] 重连只补发错过的内容,而不是重投全量
  • [remote-bridge] 会话的持久历史可分页读到第一条记录
  • [remote-bridge] 宿主帧在 frame.deflate.v1 下压缩后下发
  • [remote-bridge] 更瘦的名册字节(session.list.compact.v1)
  • [remote-bridge] 后台任务经协议下发,客户端可调用 jobs.list 并收到 jobs.changed
  • [remote-bridge] 消息增量携带参与者信息,子代理线程作为独立增量下发
  • [remote-bridge] 会话名册、历史与提示词按协议 v1 送达已配对的手机,配对靠一次性令牌加 P-256 挑战应答(延续 0.4.7 的通道,本版补齐上述能力)
  • [remote-bridge] 桥包自带家族图标与 locale 展示元数据,行在插件清单里有自己的标题与说明
  • [task-board-remote] 新增包 dsh-task-board-remote:把任务看板的能力面绑到桥接的扩展缝上,配对的手机读到同一本账(快照、验收选项、幂等动作与修订事件),全部经宿主自己的门禁
  • [task-board] 把看板的读/写面暴露给进程内消费者
  • [task-board] 卡片可设每日令牌上限,默认关闭
  • [task-board] 结算记录写明一次执行留下了什么
  • [skill-explorer] 技能中心支持筛选、折叠与导入
  • [ssh] 支持按主机选择性开启 SSH 代理转发 (#1851)
  • [usage] 用量卡用红绿标出 DeepSeek 的峰谷时段
  • [web-settings] 新增全家桶遥测开关,偏好存 localStorage 的 dsh-web-ui-telemetry-opt-out
  • [web-settings] 遥测开关沿用家族卡片外壳渲染
  • [web-all] 家族行在插件清单里以各自的库存标题与说明呈现

修复

  • [plugin-manager] 行名失效的 profile 行会被修复:读旧值、证明已安装 bundle 声明的新名、只在确有差异时改写,写前先备份
  • [plugin-manager] 更新事务清理失效行覆盖时不再误删仍在挂载的行覆盖
  • [plugin-manager] 更新门禁把「无法判定」与「DSH 版本过低」分开,不再把不可验证当成不兼容 (#1903)
  • [plugin-manager] 按宿主门禁的口径判读包声明的 dsh 范围
  • [plugin-manager] 被替换的 bundle 保持原有层序,卸载再安装不再把它挪到末尾 (#1905)
  • [plugin-manager] 桌面壳不保证重启时不再让宿主自杀
  • [remote-bridge] 能力撤下时丢弃对应的看板基线
  • [remote-bridge] 未结算的审批向声明方重放
  • [remote-bridge] 子代理线程的 catalog 条目与 spawn 关联补进基线
  • [remote-bridge] 停止应答存活探测的对端会被关闭
  • [remote-web-ui] 转发而来的请求不再被当作本机回环 (#1906)
  • [remote-web-ui] 绑定到已安装宿主接受的地址 (#1893)
  • [remote-web-ui] 下一次启动回收崩溃宿主遗留的隧道子进程
  • [dsh-update] 释放龄覆盖改用 pnpm 的 kebab-case 键,pnpm 12 不再忽略它 (#1897)
  • [win-bash] 工具输出不再带终端转义序列
  • [git-graph] 分支 chip 落在行选项之前,落在前导 chip 组里
  • [git-graph] 分支 chip 只挂在输入框的 dock 上
  • [task-board] 拒绝本构建未实现的动作类型
  • [market] market/dist 保持字节一致,部署车道与钉版对得上

其他改动

  • [perf] 聚合包的家族行改为挂载真实包,兼容层不再内联全部客户端(lib/client.js 从 2.9 MB 缩到 22 KB) (#1901)
  • [refactor] 退场插件名册由 aggregate.yml 生成,不再手写,避免与清单漂移
  • [refactor] git-graph 的分支 chip 改为按输入框 dock 挂载
  • [refactor] 桥接并入家族聚合包,并加单实例挂载守卫
  • [market] 跟随卫星仓的合并刷新皮肤、宠物、社区插件与预设的钉版
  • [satellites] 四个卫星仓的 gitlink 前移到各自的 0.4.8 发布提交
  • [libs] 重建聚合包 bundle 并刷新记录指纹
  • [test] 固定远端桥接扩展缝的规格命名、catalog 条目序列与看板套件计数
  • [test] 聚合包改为断言 shell 元数据的内容,而不是只断言存在
  • [test] 用量卡的周末与节假日谷时标记进规格
  • [test] 进程表相关的隧道用例在无法运行的环境里跳过
  • [test] 记录测试标准的 ratchet-down 基线
  • [docs] 记录 0.4.8 自动升级审计、桥协议 v1 的会话分叉与线程锚点、聚合并入决定、market/dist 不做换行归一化的原因,并同步桥接 README
  • [ci] 合并 main 与各特性分支进 dev,保持历史可读

English

This release mounts the aggregate's family rows on their real packages so the compat layer no longer ships with the aggregate and its client bundle drops from 2.9 MB to 22 KB, makes the remote bridge a row of the aggregate and keeps extending protocol v1 with forking, incremental roster and board answers, frame compression, background jobs and subagent threads, adds dsh-task-board-remote to put the task board on the remote surface, ships a family-wide telemetry switch, gives the skill center filtering, folding and importing, adds a daily token cap and a settled-execution record to the task board, brings opt-in per-host SSH agent forwarding, and marks the DeepSeek peak and off-peak clock on the usage card. The plugin manager closes three boundaries of the update path in this version: the stale row-name repair, the layer order of a replaced bundle, and telling "cannot verify" apart from "DSH too low".

Only one entry point really migrates: updating through the plugin manager (including the legacy aggregate-name migration branch) preflights, asserts the resulting version and can roll back, and it is the only path with all three. This release still has no Doctor pre-launch migration — the doctor package is only a tombstone today and the launcher consumer legacy-migration.ts refers to does not exist here — and starting the host with a direct dsh web performs no install and no preflight, so an old profile stays exactly as it is. Update through the plugin manager, or run dsh plugin --profile <name> add @linxin666/dsh-web-all@0.4.8 by hand. The dsh-update family plugin is the fourth path: it moves versions and verifies the result, but it has no backup, no preflight, no rollback and it does not repair row names.
The row-name rewrite happens on its own at startup: the 0.4.8 family rows mount under their real package names (for example @linxin666/dsh-client-ui-task-board instead of @linxin666/dsh-web-all/task-board). A profile row still carrying the old name is skipped whole for that boot, so the disabled and config the user saved stop applying — a plugin they had switched off can mount again. The plugin manager rewrites the name 1.5 s after the boot settles, and the same repair runs inside the update job; it backs the file up to cordis.patch.yml.bak-plugin-manager first, writes only on a real difference, and repeats safely. Whether a running host re-reads the file is not guaranteed, so restart once to complete the repair; nothing on disk is lost and no second row appears.
Residual warning: the plugin manager prunes the retired web-ui-model-capabilities override automatically only when the profile's bundle declarations resolve; a CLI-created profile lists @deepseek-ai/dsh-base and @deepseek-ai/dsh-web-app in dsh.profile.bundles without installing them, so the check answers unknown and the prune deletes nothing. Such a profile therefore keeps that override row on every upgrade route, a plugin-manager update included: the composition itself stays correct, and the only symptom is the repeated patch: entry "web-ui-model-capabilities" not found line on each dsh --dump-config and in CLI logs. To do it by hand: back the file up, then edit $DSH_HOME/profiles/<profile>/cordis.patch.yml and remove the entry whose id is web-ui-model-capabilities.
@linxin666/dsh-client-ui-model-capabilities stays retired with no replacement package: a custom model's input types and reasoning levels are edited on the official Models settings page. Its cordis row no longer fails the composition, it mounts nothing; the retired-plugin roster is generated from aggregate.yml's retiredPlugins: section (generated in this version instead of hand-written), the matching specifiers are skipped before loading, and the inventory shows a "Removed ..." line for each.
The remote bridge is now a row of the aggregate (web-ui-remote-bridge): a profile that finds the aggregate already carries a protocol v1 listener after the upgrade, listening on /ios/v1, plus a pairing-token endpoint on /remote-bridge/pair/token admitted only from this machine, with no separate install needed. It is single-instance against a standalone @linxin666/dsh-remote-bridge (mountOnce), so the routes are never registered twice.
The fault unit got bigger: now that family rows mount their real packages, a family plugin that fails to import or start is no longer contained as one degraded row by the fault-isolation shell; it rolls the whole loader group back. That is a deliberate trade (see the decision record for issue #1901), and the boot-audit behaviour the host owns is unchanged.
pnpm supply-chain limit: a pnpm configuration that enables minimumReleaseAge (24 hours by common default) holds an update that runs soon after publication. The remedy is minimumReleaseAgeExclude for @linxin666/* in the profile's pnpm-workspace.yaml.
The satellite ranges move in the same batch: the four satellite repositories publish 0.4.8 first, and only once the registry resolves them does the aggregate's dependency range move from ^0.4.7 to ^0.4.8:

  • Skin center (dsh-skins): @linxin666/dsh-client-ui-skin-center@0.4.8
  • Pet (dsh-pet): @linxin666/dsh-pet@0.4.8
  • Community plugin index (dsh-community-plugins): @linxin666/dsh-client-ui-community-plugins@0.4.8
  • Preset center (dsh-presets): @linxin666/dsh-client-ui-preset-center@0.4.8

New Features

  • [remote-bridge] A generic protocol extension seam: a mounted plugin can answer methods outside KNOWN_METHODS once the client declares the capability that plugin requires
  • [remote-bridge] A question request publishes the host deadline as expiresAt
  • [remote-bridge] session.fork lands with the session.fork.v1 capability, so a phone can fork a new session from an existing one
  • [remote-bridge] An agent channel covering preset enumeration and session creation
  • [remote-bridge] Incremental roster and board answers: session.list and board.snapshot answer a reset or a delta against a since token
  • [remote-bridge] Archived sessions stay out of the roster
  • [remote-bridge] A reconnect is answered with only what it missed instead of the whole baseline again
  • [remote-bridge] A session's durable history pages back to its first entry
  • [remote-bridge] Host frames ship compressed behind frame.deflate.v1
  • [remote-bridge] A slimmer roster payload behind session.list.compact.v1
  • [remote-bridge] Background jobs ride the protocol: a client may call jobs.list and receives jobs.changed
  • [remote-bridge] Message deltas carry their actors, and a subagent thread arrives as its own delta
  • [remote-bridge] The session roster, history and prompting reach a paired phone over protocol v1, pairing by a one-shot token plus a P-256 challenge-response (the channel 0.4.7 opened, completed with the capabilities above)
  • [remote-bridge] The bridge package ships the family icon and locale display metadata, so its row has its own title and description in the plugin inventory
  • [task-board-remote] New dsh-task-board-remote package: binds the task board's capability face to the bridge's extension seam, so a paired phone reads the same ledger (snapshot, acceptance options, idempotent actions and revision events) through the host's own gates
  • [task-board] The board's read/write face is exposed to in-process consumers
  • [task-board] A card can carry a daily token cap, off by default
  • [task-board] A settled execution records what it left behind
  • [skill-explorer] The skill center filters, folds and imports skills
  • [ssh] Opt-in per-host SSH agent forwarding (#1851)
  • [usage] The usage card marks the DeepSeek peak and off-peak clock in red and green
  • [web-settings] A family-wide telemetry switch, stored in localStorage under dsh-web-ui-telemetry-opt-out
  • [web-settings] The telemetry switch renders through the family card chrome
  • [web-all] Each family row appears in the plugin inventory under its own title and description

Bug Fixes

  • [plugin-manager] A profile row whose package name went stale is repaired: it reads the old value, proves the live name the installed bundle declares, writes only on a real difference, and backs the file up first
  • [plugin-manager] The update prune no longer removes a row override that is still mounted
  • [plugin-manager] The update gate tells "cannot verify" apart from "DSH too low" instead of treating an unverifiable target as incompatible (#1903)
  • [plugin-manager] The declared dsh range is evaluated the way the host gate evaluates it
  • [plugin-manager] A replaced bundle keeps its layer order instead of moving to the end through remove-then-install (#1905)
  • [plugin-manager] The host stops killing itself when the desktop shell guarantees no restart
  • [remote-bridge] A board base is dropped when its capability is withdrawn
  • [remote-bridge] Unsettled approvals are replayed to the declaring connection
  • [remote-bridge] A subagent thread's catalog entry and its spawn are tied together in the baseline
  • [remote-bridge] A peer that stops answering its liveness ping is closed
  • [remote-web-ui] A forwarded request is no longer treated as loopback (#1906)
  • [remote-web-ui] The bind is pinned to an address the installed host accepts (#1893)
  • [remote-web-ui] The next start reaps the tunnel child a crashed host left behind
  • [dsh-update] The release-age override uses pnpm's kebab-case key, so pnpm 12 no longer ignores it (#1897)
  • [win-bash] Tool output is rendered without terminal escapes
  • [git-graph] The branch chip seats before the row options group, inside the leading chip group
  • [git-graph] The branch chip mounts only on the composer input dock
  • [task-board] An action kind this build does not implement is refused
  • [market] market/dist stays byte-exact so the deploy lane matches the pins

Other Changes

  • [perf] Aggregate family rows mount their real packages, so the compat layer stops inlining every client (lib/client.js shrinks from 2.9 MB to 22 KB) (#1901)
  • [refactor] The retired-plugin roster is generated from aggregate.yml instead of hand-written, so it cannot drift from the manifest
  • [refactor] The git-graph branch chip mounts by composer dock
  • [refactor] The bridge joins the family aggregate behind a single-instance mount guard
  • [market] Follow the satellites' merges in the skin, pet, community-plugin and preset pins
  • [satellites] Move the four satellites' gitlinks to their 0.4.8 release commits
  • [libs] Rebuild the aggregate bundles and refresh the recorded fingerprints
  • [test] Pin the bridge extension-seam suite naming, the catalog entry sequence and the board suite count
  • [test] Assert the aggregate's shell metadata content instead of its existence
  • [test] Cover the usage card's weekend and holiday off-peak marker
  • [test] Skip the process-table tunnel cases where they cannot run
  • [test] Record the test-standards ratchet-down baseline
  • [docs] Record the 0.4.8 auto-upgrade audit, the protocol v1 session fork and thread anchors, the aggregate decision, and why market/dist skips newline normalization; sync the bridge README
  • [ci] Fold main and the feature branches into dev, keeping the history readable

Don't miss a new dsh-web release

NewReleases is sending notifications on new releases.