【DSH插件开发笔记】为新版本 DSH 升级了我的 dsh-lark 插件
摘要
从 settingsNamespace 导出变化导致启动失败,到旧 workspace 配置在消息处理阶段暴露问题,这是一次发生在 DSH RC 版本升级中的兼容性调试记录。

最近我在升级 dsh-lark,让它适配新版 DeepSeek Harness,也就是 DSH 0.1.2-rc.1。一开始要解决的问题很明确:旧版插件安装完成以后,DSH 无法启动。
修好启动问题以后,我用真实飞书消息继续测试,又发现旧配置里的 workspace 会让消息处理失败。下面按实际发生的顺序,记录这两个问题是怎么出现、怎么定位和怎么处理的。期望对大家有所帮助。
第一个问题是 DSH 无法启动
旧版 dsh-lark 0.2.2 安装到 DSH 0.1.2-rc.1 后,进程会在加载插件入口时退出:
SyntaxError: The requested module '@deepseek-ai/dsh-settings'
does not provide an export named 'settingsNamespace'
这个错误发生得很早。飞书 WebSocket 还没有连接,App ID、App Secret 和消息处理逻辑也没有开始运行。
我最初在已有开发仓库里启动插件,问题没有稳定出现。这个环境装过多个版本的 DSH 依赖,Node.js 的模块解析可能从仓库上层找到旧包,掩盖了真实的安装结果。
于是我换了一个全新的 DSH 配置目录,只安装 npm 上的 dsh-lark 0.2.2,再启动 DSH 0.1.2-rc.1。错误每次都能复现,范围也缩小到了 @deepseek-ai/dsh-settings 的运行时导出。
源码与发布包并不完全相同
在 DeepSeek Harness 仓库的源码里,可以找到 settingsNamespace。但用户安装的是 npm 发布包,不是仓库里的 TypeScript 源文件。
我检查了 @deepseek-ai/dsh-settings@0.1.2-rc.1 的实际 tarball。它生成的 lib/index.js 没有导出 settingsNamespace。dsh-lark 在运行时导入这个名字,ES Module 加载器因此直接终止了启动。
settingsNamespace 在这里主要用于给字符串增加类型约束。插件使用的 namespace 一直是 lark-channel,没有必要为了得到这个固定值保留一项运行时依赖。修复方式是只导入类型:
import type { SettingsNamespace } from '@deepseek-ai/dsh-settings'
const LARK_SETTINGS_NAMESPACE = 'lark-channel'
const namespace = LARK_SETTINGS_NAMESPACE as SettingsNamespace
settings.register(namespace, provider)
TypeScript 在构建时会移除 type-only import。类型检查仍然存在,生成的 JavaScript 不再要求发布包提供 settingsNamespace。
同一轮升级还有两处接口变化
启动问题解决以后,我继续检查 DSH 新旧版本之间的调用差异。
Session 事件在旧版中通过 session.events 读取,新版提供 session.snapshotEvents()。插件在 Harness 边界做了一层兼容,根据当前 Session 暴露的接口选择读取方式:
function eventsFrom(session: Session, firstSeq: number) {
return 'snapshotEvents' in session
? session.snapshotEvents(firstSeq)
: session.events
}
凭证更新事件也从 credentials/updated 变成了 credentials/reference-updated。dsh-lark 同时监听两个事件名,让旧版和新版宿主都能在 App Secret 变化后重新建立连接。
这几个改动一起进入了 dsh-lark 0.2.3。我完成单元测试、类型检查和构建以后,又执行 npm pack,把真实 tarball 安装到新的 DSH profile。Web 服务成功启动,原来的 settingsNamespace 错误消失。
启动以后,第二个问题才出现
DSH 能启动还不代表飞书消息已经可以完整处理。我启动新版插件,看到 WebSocket 正常连接:
[info]: [ '[ws]', 'ws client ready' ]
随后从飞书发送一条消息,终端又出现了新的错误:
dsh-lark: message handling failed:
ENOENT: no such file or directory, realpath 'dsh-lark'
这次错误不在插件加载阶段,而是在收到消息以后创建 Harness Session 的过程中。dsh-lark 读取到配置中的 workspace dsh-lark,随后把它传给 workspaceRegistry.resolveByPath():
const workspace = this.config.workspace === undefined
? this.deps.workspaceRegistry.list()[0]
: await this.deps.workspaceRegistry.resolveByPath(this.config.workspace)
Workspace Registry 会使用 Node.js 的 fs.realpath() 规范化路径。由于 dsh-lark 是相对路径,它会相对于 DSH 进程的当前工作目录解析。这次 DSH 从用户主目录启动,实际查找的是类似 /Users/name/dsh-lark 的位置。真实仓库并不在那里,于是得到 ENOENT。
进一步比较代码后可以确认,这不是新版 DSH 改变了 workspace 路径解析机制。dsh-lark 0.2.2 与 0.2.3 使用相同的 resolveByPath() 调用,@deepseek-ai/dsh-workspace 的 0.1.0-rc.7 与 0.1.2-rc.1 也都采用 fs.realpath()。0.2.3 在这一部分真正改变的是 Session 事件读取:从直接访问 session.events,改为兼容新版的 session.snapshotEvents()。
更准确地说,这个相对路径本来就依赖 DSH 的启动目录,只是此前没有在相同条件下暴露。DSH 当时可能从包含 dsh-lark 仓库的父目录启动,相对路径恰好可以解析;也可能已经存在对应的 live Agent,插件在解析 workspace 之前就直接复用了它。现有运行记录不足以确定当时属于哪一种情况,因此不能把问题归因于新版 DSH 的路径解析变化。
把 Settings 中的 Workspace 改成真实存在的绝对路径,或者清空并使用 DSH 已注册的默认 Workspace,消息就可以继续处理。例如:
/Users/name/github/project
这个配置来自旧版本,并在插件更新时被保留下来。保留 Settings 和 Credentials 本身是正确的升级行为;真正的问题是配置中保存了一个含义会随进程工作目录变化的相对路径。
这个错误还可以更早告诉用户
目前 workspace 错误要等到第一条消息到达以后才出现,而且终端显示的是底层 realpath 异常。对用户来说,更合适的时机是在 Settings 页面保存配置时。
插件后续可以在保存时检查 workspace:路径是否为绝对路径、目录是否存在、DSH 的 workspace registry 是否能够解析。输入框也可以从自由文本改成已注册 Workspace 的选择器,并显示最终使用的绝对路径。
终端仍然可以记录具体错误,飞书回复则不应暴露本机目录。比较合适的提示是“当前 Workspace 不可用,请在 DSH 的飞书与 Lark 设置中检查”。这样管理员知道去哪里修,聊天用户也不会看到本地文件系统信息。
RC 阶段的兼容问题
DeepSeek Harness 目前仍处在早期 RC 阶段。包的导出、Session 接口、事件名称和 Profile Bundle 的装配方式继续变化,是这个阶段自然会发生的事情。
对外部插件来说,不能假设相邻 RC 版本一定兼容,也不能只根据主仓库源码判断发布包的行为。这次开发里,真正有用的验证顺序是:在空目录复现、检查 npm tarball、安装插件产物、启动真实 DSH,最后再发送一条消息走完 Session 创建和回复流程。
目前 npm 上的最新版是 dsh-lark 0.2.4,包含前述 DSH 兼容修复。已有用户可以这样更新:
npx @deepseek-ai/dsh plugin --profile web update --latest @sugarforever/dsh-lark
更新后重启 dsh web。如果终端已经出现 ws client ready,但发送消息后报告 workspace 的 realpath 错误,就到 Settings 中把 Workspace 改成绝对路径或清空。
- 插件源码:github.com/sugarforever/dsh-lark
- DeepSeek Harness:github.com/deepseek-ai/deepseek-harness
相关文章
2026年9月6日
我给 DeepSeek DSH 做了个 zvec-grep 插件
从一次代码语义搜索实验,到一个自动索引、可见状态、后台增量更新的 DeepSeek DSH 插件,我在 dsh-zvec-grep 开发中做了哪些取舍。
2026年10月1日
OpenRig 冲上 Trending:把 Claude Code 和 Codex 编成一支队伍
OpenRig 在 GitHub Trending Today 获得 622 个 stars 的窗口信号。它用可恢复的队列、席位与权限记录,把 Claude Code 和 Codex 放进同一套多 Agent 工程流程。
2026年9月30日
NVIDIA OpenShell,把 Agent 的权限写进策略
OpenShell 在 GitHub Trending Today 获得 978 个 stars 的窗口信号。它把文件、网络和凭证边界做成可执行策略,适合开始让 Agent 接触真实开发环境的团队。
最近一封 · Sample
OpenRig 冲上 Trending:把 Claude Code 和 Codex 编成一支队伍
“OpenRig 在 GitHub Trending Today 获得 622 个 stars 的窗口信号。它用可恢复的队列、席位与权限记录,把 Claude Code 和 Codex 放进同一套多 Agent 工程流程。”
—— william
来信
里面装的是
- 新文章 — 写完一篇就寄一封,不攒货
- 这周读到的、看到的、好用的工具
- 正在折腾的实验,附带翻车记录
约莫 1–2 周一封 · 随时退订
合作伙伴
CompeteMap — 英国及爱尔兰学生竞赛一站式搜索
数学、编程、科学、写作等各类竞赛信息汇总,支持按年龄和科目筛选,再也不错过报名截止日。