环境
- 插件:
@feiyang666/dsh-usage-plugin v1.9.1
- DSH:Web 版(profile: web),Windows 10
- 会话工作区:
%USERPROFILE%\Documents\dsh
问题现象
-
每次启动 DSH 后、在产生新对话之前,插件读取/写入的用量文件是用户主目录下的
%USERPROFILE%\dsh-usage\usage-records.json,而不是工作区路径。
-
一旦产生新对话(会话绑定工作区)后,插件切换为读写
%USERPROFILE%\Documents\dsh\dsh-usage\usage-records.json。
-
统计页顶部显示:
数据持久化:%USERPROFILE%\dsh-usage\usage-records.json(每次调用实时落盘,插件重启后自动恢复,最多保留 100000 条)
但这条路径只是启动时的临时根目录,会话激活后真实数据落在工作区文件里——UI 显示的持久化路径与实际落盘路径不一致。
-
重启后历史用量数据"消失",统计页显示 0:历史数据躺在其它工作区/路径的 dsh-usage/usage-records.json 里,重启时插件只读取当前选中根目录的那一份,不跨路径合并,因此总量被拆散。
证据
机器上同时存在多份 usage-records.json(均为本插件产生):
| 路径 |
状态 |
%USERPROFILE%\dsh-usage\usage-records.json(含 .keep 占位文件) |
启动时根目录解析为主目录时创建,含本会话 19:37–20:08 的记录,之后不再增长 |
%USERPROFILE%\Documents\dsh\dsh-usage\usage-records.json |
会话激活后持续写入(观察期间 9601→11203 字节持续增长) |
%USERPROFILE%\.dsh\dsh-usage\usage-records.json |
内容为 [],更早一次根解析的残留 |
根因分析
存储根目录的解析完全取决于会话工作区/cwd,而非固定路径(lib/index.js):
initPersistence() 的候选根依次为:agent.session.header.cwd → sandboxPolicy.workspaceRoot → 由 fs.resolve('dsh-usage-probe') 推导的 cwd。
- 启动时还没有会话工作区,
workspaceRoot 解析到用户主目录 %USERPROFILE%,于是根被设为该目录,读写 %USERPROFILE%\dsh-usage\usage-records.json。
- 会话激活后,
ensureSessionRoot() 发现 policy.workspaceRoot 变为工作区路径,调用 tryInitWithRoot(工作区) 把根切换为工作区,开始读写 Documents\dsh\dsh-usage\usage-records.json。
tryInitWithRoot() 切换根时会把内存中的 records 整体写回新路径,但不做跨根的数据合并/迁移;重启后 initPersistence() 只读取它当前选中根(主目录)的那一份文件。
客户端渲染(lib/client.js 约 1145 行):
el("div", { style: st.note }, "数据持久化:" + dataPath + "(每次调用实时落盘,插件重启后自动恢复,最多保留 100000 条)")
其中 dataPath 来自 /usage/api 返回的当前根路径,随根切换而变化,因此显示值不可靠。
预期行为 vs 实际行为
- 预期:持久化路径固定且唯一;UI 显示的路径与真实落盘路径一致;重启后自动恢复全部历史记录(无论记录之前落在哪个根)。
- 实际:持久化路径随"是否有会话/会话工作区"漂移;UI 显示的路径与实际落盘路径不一致;历史记录被拆散在多份文件中,重启后仅恢复当前根的那一份 → 表现为"数据消失/统计为 0"。
建议修复方向
- 支持固定持久化目录配置:允许用户(或默认)指定一个稳定的目录存放
usage-records.json,不再随工作区漂移(例如固定到 $DSH_HOME/dsh-usage/ 或用户配置目录)。
- 启动时合并去重:若确实按工作区分目录存储,则启动时应把"主目录 + 当前工作区 + 配置目录"等所有已知位置的记录读取合并(现有
normalizeRecord + 按 time 去重的逻辑可直接复用),而不是只读当前根。
- UI 路径与真实路径保持一致:统计页显示的"数据持久化"路径应始终等于实际写入路径;若根会切换,应同步更新并说明。
- (可选)切换根目录时对旧根做一次真正的数据迁移,避免复制/分裂。
复现步骤
- 以某个工作区启动 DSH,安装并启用本插件。
- 未发送任何对话前,查看统计页:显示"数据持久化:
<主目录>\dsh-usage\usage-records.json"。
- 发送一条消息产生用量记录,再次查看:此时记录实际写入
<工作区>\dsh-usage\usage-records.json。
- 重启 DSH 后查看统计:仅恢复主目录文件中最后写入的那部分记录,其余历史(落在工作区/其它路径的)不显示,表现为统计大幅减少或为 0。
环境
@feiyang666/dsh-usage-pluginv1.9.1%USERPROFILE%\Documents\dsh问题现象
每次启动 DSH 后、在产生新对话之前,插件读取/写入的用量文件是用户主目录下的
%USERPROFILE%\dsh-usage\usage-records.json,而不是工作区路径。一旦产生新对话(会话绑定工作区)后,插件切换为读写
%USERPROFILE%\Documents\dsh\dsh-usage\usage-records.json。统计页顶部显示:
但这条路径只是启动时的临时根目录,会话激活后真实数据落在工作区文件里——UI 显示的持久化路径与实际落盘路径不一致。
重启后历史用量数据"消失",统计页显示 0:历史数据躺在其它工作区/路径的
dsh-usage/usage-records.json里,重启时插件只读取当前选中根目录的那一份,不跨路径合并,因此总量被拆散。证据
机器上同时存在多份
usage-records.json(均为本插件产生):%USERPROFILE%\dsh-usage\usage-records.json(含.keep占位文件)%USERPROFILE%\Documents\dsh\dsh-usage\usage-records.json%USERPROFILE%\.dsh\dsh-usage\usage-records.json[],更早一次根解析的残留根因分析
存储根目录的解析完全取决于会话工作区/cwd,而非固定路径(
lib/index.js):initPersistence()的候选根依次为:agent.session.header.cwd→sandboxPolicy.workspaceRoot→ 由fs.resolve('dsh-usage-probe')推导的 cwd。workspaceRoot解析到用户主目录%USERPROFILE%,于是根被设为该目录,读写%USERPROFILE%\dsh-usage\usage-records.json。ensureSessionRoot()发现policy.workspaceRoot变为工作区路径,调用tryInitWithRoot(工作区)把根切换为工作区,开始读写Documents\dsh\dsh-usage\usage-records.json。tryInitWithRoot()切换根时会把内存中的records整体写回新路径,但不做跨根的数据合并/迁移;重启后initPersistence()只读取它当前选中根(主目录)的那一份文件。客户端渲染(
lib/client.js约 1145 行):其中
dataPath来自/usage/api返回的当前根路径,随根切换而变化,因此显示值不可靠。预期行为 vs 实际行为
建议修复方向
usage-records.json,不再随工作区漂移(例如固定到$DSH_HOME/dsh-usage/或用户配置目录)。normalizeRecord+ 按time去重的逻辑可直接复用),而不是只读当前根。复现步骤
<主目录>\dsh-usage\usage-records.json"。<工作区>\dsh-usage\usage-records.json。