DSH 0.1.2-alpha 系列升级踩坑全记录(3 次升级全挂的完整根因分析 + 官方源码佐证) #5300
LiuJunheng
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
总览
0.1.2-alpha 系列迭代节奏较快,四次升级遇到四种不同类型的启动/功能故障。共性是:不是某个插件不兼容,而是框架自身的契约在变,且 changelog 未标注为破坏性变更。
以下按时间顺序倒出每次故障的症状、根因、官方源码佐证和解决方案。
升级 1:rc.7 → 0.1.2-alpha.1(症状:服务秒退,退出码 1)
现象:升级后启动立即退出,server.log 里反复出现:
或者 403 /api 拒绝、局域网文件浏览 pickDirectory 不工作、会话记录 UUID 生成报错等。
根因:pnpm 工作区策略 + client-connection 信任围栏,两处同时收紧。
改动 1 —
autoInstallPeers: false导致 peer 依赖不装新版
pnpm-workspace.yaml把autoInstallPeers从true改成了false。这意味着@deepseek-ai/dsh-session等框架包声明的 peer 依赖(@deepseek-ai/cordis、@deepseek-ai/dsh-scope、@deepseek-ai/dsh-invariants)不会再自动安装到 profile 的 node_modules。插件
import { ... } from '@deepseek-ai/cordis'时找不到包 → 整个插件树秒退。修复:在 profile 的
package.json→dependencies里显式声明这些 peer,版本号对齐核心版本(如cordis@4.0.2、dsh-scope@0.1.2-alpha.2、dsh-invariants@0.1.2-alpha.2),然后pnpm install --force --no-frozen-lockfile重建依赖树。改动 2 — Chrome 150+ /api 403(hostname → host 精确比较)
client-connection的isTrustedApiRequest()函数,信任比较从origin.hostname === hostUrl.hostname改成了origin.host === hostUrl.host。问题:浏览器 Origin 不带端口(
http://127.0.0.1),但hostUrl.host带端口(127.0.0.1:3080),精确比较永远不相等 → 全 /api 403。触发条件:Chrome 150+ + 局域网 IP 访问 / 非标准端口。127.0.0.1 + 默认端口下 Chrome 仍会放行 loopback,不一定命中。
修复:patch
client-connection的isTrustedApiRequest(),把比较改回 hostname 级别(忽略端口差异)。改动 3 —
trustedHosts白名单收紧host.pickDirectory等特权 API 的信任校验,从「loopback 白名单」收紧到了显式trustedHosts配置。不在trustedHosts里的局域网地址,即使是同机不同网卡 IP,也会被拒绝。改动 4 —
crypto.randomUUID在 http + 非回环 IP 不可用randomUUID()浏览器 API 要求 secure context(https 或 localhost)。http + 非回环 IP 下调用会抛TypeError。部分插件用它生成会话 ID / 工作区标识 → 静默出错。修复:前端注入一个基于
crypto.getRandomValues()的 polyfill。升级 2:alpha.1 → alpha.2(症状:消息行空了 + 裸地址 401)
两个症状同时出现:
undefined或者直接不渲染;http://127.0.0.1:3080/返回 401,提示dsh web authentication required。根因:两个独立的框架层改动撞一起了。
改动 1 — 会话快照重构(useSession → useChat)
0.1.2 把
useSession的返回值从「整包会话」拆成了两层:旧插件写
useSession(s => s.nodes)→ 新版s.nodes === undefined→ 整个 UI 静默拿不到数据。不是报错,是悄悄空掉。排查时容易误以为是 token 计算或渲染逻辑的问题,实际是数据根本没传过来。还有一个隐藏坑:聊天 UI 组件换包了。
turnTail/assistant-actions/conversation.chat.node这些插槽从dsh-client-ui-conversation迁到了dsh-client-ui-chat。旧版 import 路径虽然还能 resolve(conversation 包没删),但里面的组件已经不导出了——同样不报错,静默没内容。兼容写法(两端都能用):
改动 2 — 强制浏览器认证(BrowserAuth)
alpha.2 新增了 BrowserAuth:首次访问需要启动时打印的一次性
?token=<launchToken>地址换 30 天 Cookie,裸地址直接 401。Config 里没有enableAuth开关——关掉只能 patch 源码的两个点:requestRejection():去掉browserAuth.isAuthenticated检查;authorizeIndex():提前 return true 跳过 token 校验。注意:关掉的只是 401 那层,403 的 Host/Origin 围栏还在。局域网模式下 loopback 自动放行,所以 127.0.0.1 其实没差别;0.0.0.0 的话等于开了裸奔窗口——生产环境别关。
升级 3:alpha.2 → alpha.3(症状:全插件等 webServer)
现象:启动日志刷屏
根因:框架不再自动注入 core bundles。
旧版本(包括 alpha.2)启动时,框架会自动把
@deepseek-ai/dsh-base(timer/llm/session 等基础服务)和 profile 层(dsh-web-app提供 webServer、dsh-headless提供 headless runner)隐式塞进 patch 栈。profile 的dsh.profile.bundles只写自己的插件就行。alpha.3 起必须显式声明在 bundles 数组里。旧 profile 升级后 bundle 栈底层 = 空 → 所有插件依赖的核心服务不存在 → 全挂。
官方升级盲区(源码佐证)
官方
dsh-app-boot/lib/index.js里有个normalizeShippedProfile函数试图自动兜底升级,但实际兜不住:三种旧版用户全被跳过:
bundles字段不存在(undefined)bundles是空数组[]bundles = ["my-plugin"](有自己装的插件)全新安装不会中——
PROFILE_TEMPLATES正确声明了 core bundles(["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app"])。但升级用户 100% 中招,除非 bundles 恰好等于某个已退役元组。修复方法
在
$DSH_HOME/profiles/web/package.json的dsh.profile.bundles数组最前面加上 core bundle:{ "dsh": { "profile": { "bundles": [ "@deepseek-ai/dsh-base", // ← 加在最前面(patch 栈底层) "@deepseek-ai/dsh-web-app", // ← web profile 需要 "dsh-my-awesome-plugin", // ... 你的其他插件 ] } } }关键认知:core bundles 只需要在
dsh.profile.bundles数组里出现即可,不需要写进 dependencies。它们在 runtime 的 node_modules 里已经装好了(框架自带),DSH 加载 bundle 时直接按名字在 runtime 的@deepseek-ai/目录下找,不走 profile 的 node_modules。给框架作者的建议
Changelog 请标注「需要用户改动的破坏性变更」。三次升级涉及的 peer 策略变化、useChat 重构、core bundle 显式声明,都是直接改插件/配置契约,但 changelog 只写了「refactored」「improved」,社区插件作者没感知,全部静默失效。
自动注入 vs 显式声明最好给个过渡。alpha.3 从「自动注入」直接切到「必须显式」,中间没有 deprecated warning。如果框架能在启动时检测 bundles 缺失 core 并打印一个「你的 profile 缺少 XXX core bundle,将自动注入——但建议你显式声明」的 warning,就不会整挂一轮。
normalizeShippedProfile的升级判断逻辑太窄。当前只处理 retired tuple 和 patchReload 缺省,没覆盖最常见的两种旧 profile(bundles undefined / 空数组)。建议加一条:如果 bundles 长度 < 模板 bundles 长度,自动补齐缺失项。peer 依赖收紧最好有迁移提示。
autoInstallPeers: false后第一次启动时,框架可以扫描所有@deepseek-ai/*包的 peer 依赖,把缺失的列个 warning 打到 stderr,而不是等插件 import 时报not in cache后秒退。社区插件作者快速自查清单(从 alpha.1 / rc.7 升上来的)
peer 依赖补齐——profile
dependencies里有没有cordis、dsh-scope、dsh-invariants?缺一个就秒退(alpha.1 起)检查
useSession返回值——还在读s.nodes吗?换成useChat(s => s.legacy.nodes)(alpha.2 起)检查 UI 组件 import——
dsh-client-ui-conversation里的turnTail/assistant-actions还能 resolve 吗?换dsh-client-ui-chat(alpha.2 起)profile 的
dsh.profile.bundles里有没有@deepseek-ai/dsh-base?alpha.3+ 必须显式写启动后裸地址 401?是 BrowserAuth,要么带 token 地址打开,要么 patch 关掉(不推荐)
All reactions