openharness-reader
OpenHarness Reader —— DeepSeek Harness Web 的工作区文件阅读/编辑器插件(平行项目,非壳功能)。
规划文档:000-file-editor-plugin.md(本仓库内跟踪)。
路线
| 里程碑 | 内容 | 状态 |
|---|---|---|
| M1 | 骨架:package.json + cordis.patch.yml + host/client 两半 + esbuild;注册 details 面板(占位) | ✅ 2026-08-14 |
| M2 | 文件浏览:host fs RPC(list/stat/read)+ client 树/列表 | ✅ 2026-08-14(v0.2.0) |
| M3 | 编辑保存:CodeMirror 6 + ctx.fs 写回(版本守卫 + 冲突提示) |
✅ 2026-08-14(v0.2.0) |
| M4 | Markdown 渲染预览(GFM + 高亮) | ✅ 2026-08-14(v0.2.0) |
| 挂载方案改方案 1 | 彻底弃用 details 槽:client 改为 body portal + fixed 定位面板(照 DSH-better-sidebar);per-session store 管布局;apply 全包 try/catch + 错误条兜底;唯一官方槽位 = settings.section(list 槽) |
✅ 2026-08-14(v0.3.0) |
| v0.4.0 | 样式参考 dsh-better-sidebar:共享宽度布局(#root margin-right 推挤,非浮层覆盖)、VSCode 式文件标签页、文件夹/文件类型图标(sidebar 同款 glyph)、搜索栏置顶 + 「隐藏」checkbox、全量 i18n |
✅(v0.4.0) |
| v0.4.1 | 跟随 dsh rc.7 插件设置机制:host 半用 installSettingsSection 注册 openharness-reader 设置命名空间(maxReadBytes 变为用户可配置);浏览器半注册 settings.plugin.item keyed 卡片(rc.7「插件配置」标签页),替换并删除旧 settings.section 分区;peer 范围 ^0.1.0-rc.6 兼容 rc.6/rc.7 |
✅(v0.4.1) |
| v0.4.2 | 折叠按钮右上偏移(top:96px → 20px);README 新增「方式一:App 插件中心 / 方式二:命令行」安装 + 升级/卸载/本地开发/验证章节 |
✅(v0.4.2) |
| v0.4.3 | README 安装章节补 DSH App 下载链接(GitHub Releases) | ✅(v0.4.3) |
| M5 | npm 发布 + 壳 AUTO_INSTALL_PLUGINS 预装 |
⏳ |
功能(v0.4.1 · 样式参考 dsh-better-sidebar)
样式参考:本插件的布局与视觉参考了 dsh-better-sidebar(MIT)——共享宽度推挤(
#root { margin-right: var(--ohr-width) },非浮层覆盖)、VSCode 式标签页、文件夹/代码文件类型图标(与 sidebar 的IconFolderOpen16/IconFolderClose16/IconCodeOutline16同款 glyph,内联 SVG 实现)、34px→28px 紧凑标签条、--dsw-alias-*主题 token 驱动。
- 共享宽度(v0.4.0):面板不再浮在 WebUI 上方,而是与 DSH 应用共享宽度——展开时
#root让出面板宽度(对话区被挤窄),折叠时恢复全宽;z-index:40低于应用弹出层。标题栏右侧红色×收起面板;折叠后右侧靠上显示可展开的竖条。 - 文件标签页(v0.4.0):树里点击文件以 VSCode 式标签页打开(顶部标签条),多文件并行、按路径去重(同文件聚焦、新文件新标签);每个标签独立保留未保存内容/dirty/编辑预览模式/冲突状态;可关闭标签,关闭全部回到文件树;首格为固定的「文件浏览」标签。
- 文件浏览:面板按当前会话 workspace(cwd)懒加载目录树;目录点击展开(文件夹图标开/合)、文件点击打开(代码文件图标);搜索栏置顶(在 Files/根目录标签上方);「隐藏」checkbox 默认勾选(隐藏
.git等 dotfiles,取消勾选显示);目录优先排序;面板拖拽调宽 / 收起 / 窄屏抽屉(per-session 布局)。 - 编辑保存:CodeMirror 6(JS/TS/JSON/Markdown/Python/CSS/HTML/XML + shell/yaml/toml/sql 等 legacy 模式);保存走
ctx.fs.writeText,以读取时版本做replaceIfVersionCAS —— 磁盘文件被外部(如 agent 工具)改动时弹出冲突对话框:重新加载 / 覆盖保存。 - Markdown 预览:marked(GFM:表格/删除线/自动链接)+ highlight.js 代码高亮;
.md文件支持 编辑 / 预览 切换;预览容器带data-adhdgofly-highlight属性契约,已装 adhdgofly-dsh-ext ≥ v0.1.2 时自动获得词性高亮(名绿/动红/形紫/其他灰,与对话共享色板/筛选/主题)。 - i18n(v0.4.0):全部 UI 文案(面板/标签/搜索/隐藏/编辑器/保存/冲突/设置)走
src/client/locales.ts字典,随页面语言(<html lang>,zh → 中文,其他 → 英文)自动切换。 - 树滚动(v0.3.3 修复):浏览器视图为 flex 列(
.ohr-browser),文件树overflow:auto真正生效,深目录可上下滚动;编辑器视图滚动不受影响。 - 展开折叠样式(对齐 VS Code)+ 修复/隐藏:目录图标用文件夹开/合表达展开态;v0.3.5 修复「展开后收不回」(递归错用父级开合态致
.git子项恒展开,已改按当前 entry 自身状态);dotfiles(.git/.DS_Store)默认隐藏,「隐藏」checkbox 控制显示。方案见001-tree-collapse-style.md。 - 挂载稳定(v0.3.4 修复):面板 Shell 的
useSyncExternalStore无条件调用(修复 hook 数量突变导致的Uncaught TypeError: Cannot read properties of undefined (reading 'length')——sessions 服务晚就绪时面板不挂载/半挂载),会话何时就绪都能稳定显示。 - 只读保护:超过
maxReadBytes(默认 5 MB)的大文件与二进制文件(FS_NOT_TEXT)只读打开。 - 设置卡片(v0.4.1,跟随 dsh rc.7):
maxReadBytes作为用户可配置项出现在 设置 → 插件 → 插件配置 标签页(settings.plugin.itemkeyed 卡片);改完保存即持久化到 profile,刷新仍在。卡片自带 revision 设栅——并发修改会冲突重读而非静默覆盖。 - 策略一致:host 读写全部走
ctx.fs缝隙,并派发fs/write-intent/fs/edit-intent/fs/observed事件(actor 携带会话),与 agent 的 fs 工具共享观察状态与守卫。
架构
- host half(
src/host/index.ts):注册通用 Connection RPC 通道/openharness-reader(ctx.connection.rpc.handle,authorityloopback,与/api同款浏览器信任围栏)。端点:list/stat/read/write/edit。业务失败以稳定FsErrorCode结构化返回。entry 必须在cordis.patch.yml声明inject: ['fs', 'connection'](v0.3.1 修复:插件 entry ctx 用ctx.get拿不到兄弟 entry 提供的服务,apply静默 bail → 路由不注册 → 所有调用落静态 fallback 405)。v0.4.1 起用installSettingsSection注册openharness-readersettings 命名空间(schema 仅maxReadBytes,schemastery 单一真源);运行时读取切到 resolved scope(无 settings 服务时回退 entry,行为不变)。 - client half(
src/client/):body portal(v0.3.0 方案 1)+ 共享宽度布局(v0.4.0 方案 A,样式参考 dsh-better-sidebar)。apply()自建document.createElement('div')(带data-openharness-reader)挂到document.body,createRoot(host).render(<ReaderPanel/>);面板 fixed 定位(默认右侧),展开时通过--ohr-widthCSS 变量推挤#root(margin-right),与 DSH 应用共享宽度而非覆盖。布局状态(宽/折叠/窄屏抽屉)在 per-session store(src/client/layout.ts);ctx.connection.rpc.call调 host;ctx.sessions.list取当前会话 workspace。 - 设置卡片(v0.4.1):
settings.plugin.item(keyed 槽,key =openharness-reader,src/client/settings-card.tsx),经ctx.get('settingsScope').bind({namespace})绑定命名空间(dsh-client-ui-settings提供,见dsh.client.inject),自研带标题栏的可折叠卡片外壳;scope.set写入宿主持久化,revision 栅栏由 scope 自动处理;绝不注册details。 - 协议类型共享于
src/shared/protocol.ts。
安装
前置:本机已安装 DeepSeek Harness(DSH),且已初始化 web profile(npx @deepseek-ai/dsh web 跑过一次即可)。当前版本 0.4.3。
方式一:App 插件中心(推荐,无需命令行)
- 下载并安装 DSH 桌面 App(GitHub Releases · openharness,下载对应系统安装包),打开后在左侧边栏进入「插件中心」;
- 在「特别推荐」里找到 openharness-reader;
- 点击「安装」,等待安装完成——DSH 会自动重启并应用,无需手动操作;
- 重启后即可在 Web 右侧看到「Reader」文件浏览/编辑面板(见 §功能)。
方式二:命令行安装
# 1) 安装 DSH(首次)
npx @deepseek-ai/dsh web
# 2) 安装插件
npx @deepseek-ai/dsh plugin --profile web add openharness-reader
# 若 pnpm ≥ 10 报 ERR_PNPM_ADDING_TO_ROOT,命令末尾加 -w:
npx @deepseek-ai/dsh plugin --profile web add -w openharness-reader
⚠️ 命令行安装后必须重启 DSH 才生效
- 在正在运行的 DSH Web 终端按
Ctrl+C停止当前进程;- 重新启动:
npx @deepseek-ai/dsh web
(若机器已把dsh装进 PATH,dsh web/dsh --profile web与之等价,任选其一)- 等 Web 界面重新打开后刷新浏览器页面。
不重启的话,正在运行的 DSH 进程的 boot 图里没有本插件,刷新页面也不会加载它。
npx @deepseek-ai/dsh plugin add 会:
- 在
~/.dsh/profiles/web里pnpm add本包; - 自动 reconcile:检测到
dsh.bundle声明,把openharness-reader追加进dsh.profile.bundles,成为 profile 补丁层。
升级 / 卸载
# 升级到最新版(当前 0.4.3)
npx @deepseek-ai/dsh plugin --profile web update openharness-reader
# 卸载
npx @deepseek-ai/dsh plugin --profile web remove openharness-reader
- 命令行升级/卸载后,同样需要
Ctrl+C停止 →npx @deepseek-ai/dsh web重启 → 刷新浏览器。 - App 插件中心里升级/卸载会自动重启并应用。
版本要求:设置卡片(
settings.plugin.item,v0.4.1)需 dsh>=0.1.0-rc.6
(rc.6+ 内置该设置槽)。更早版本不显示设置卡片,但插件其它功能不受影响。插件
peer 范围^0.1.0-rc.6同时覆盖 rc.6 / rc.7,安装零警告、不强制升级。
本地开发安装(file: 符号链接,改代码无需重装)
# 在插件仓库目录或其父目录执行(file: 相对路径以调用目录为锚)
npx @deepseek-ai/dsh plugin --profile web add file:../openharness-reader
file: 依赖是符号链接:客户端改动 npm run build 后刷新页面即可(DSH Web 无 HMR);patch / bundles 改动需重启 profile。安装后同样需要按上面的提醒重启一次才生效。
验证
npx @deepseek-ai/dsh --profile web --dump-config | grep -A3 openharness-reader # 合成树里应有插件行
# 浏览器 DevTools → Network 应能看到 /plugins/openharness-reader/client.js 正常返回
开发
npm install
npm run build # esbuild 双入口(lib/client.js + lib/index.js)
npm run typecheck # tsc --noEmit
npm test # 冒烟(bundle 形态)+ host RPC 行为测试(stub ctx)
发布流程见
010-dsh-plugin-publish.md;安装/升级/卸载见上文 §安装。
说明
- v0.3.0 挂载方案(方案 1,定案):
details是 single slot(dsh-client-ui-layout声明,kind: single / scope: session)——同一 priority 只允许一个注册,重复注册抛错,而 client web 的 boot sweep 对 apply 失败的 entry fail-loud(整机白屏 "HARNESS / Failed to load plugins")。v0.2.1 的「priority 探测 − 1」只是规避,仍依赖与 details 占用者的博弈。v0.3.0 起彻底弃用details槽:改 body portal + fixed 定位面板(照 DSH-better-sidebar),对任何 details 占用者(含 x6)零交集、零冲突,不依赖 priority 探测、不依赖加载顺序;唯一官方槽位仅限 list/keyed 槽(settings.section,带id+order,list 槽消费全部 entry、注册永不冲突);整个 apply 包 try/catch,任何阶段失败只降级为「面板不显示 + console.error + 页面角落错误条」,绝不把错误抛出 apply 拖垮 boot。详见000-file-editor-plugin.md§三/§五。 - 与
adhdgofly-dsh-ext高亮的关系(方案 A′ 属性契约,v0.3.3 + adhdgofly v0.1.2 已落地):代码内容与编辑器(contenteditable)天然被其跳过;MD 预览容器带data-adhdgofly-highlight属性,adhdgofly 每次处理自动采纳该容器(无需写containers配置、无需重载),未装 adhdgofly 时属性惰性无害。详见000-file-editor-plugin.md§五。
我开发的 DSH 插件
我(zuoguyoupan2023)开发维护的一系列 DeepSeek Harness(dsh)插件,均可按需通过 dsh plugin --profile web add <name> 安装:
| 插件 | 作用 | GitHub 仓库 | 安装 |
|---|---|---|---|
| adhdgofly-dsh-ext | DSH Web 界面词性高亮(名绿/动红/形紫/其他灰) | zuoguyoupan2023/adhdgofly-dsh-ext | dsh plugin --profile web add adhdgofly-dsh-ext |
| openharness-reader | 工作区文件浏览/编辑 + Markdown 预览 | zuoguyoupan2023/openharness-reader | dsh plugin --profile web add openharness-reader |
| openharness-reply-in-cn | 强制模型用简体中文回复,侧边栏「中文回复」项 | zuoguyoupan2023/openharness-reply-in-cn | dsh plugin --profile web add openharness-reply-in-cn |
| openharness-rule-for-dsh-plugin | 注入 DSH 插件开发的 CAN/SHOULD/MUST NOT 规范,侧边栏「插件开发规范」项 | zuoguyoupan2023/openharness-rule-for-dsh-plugin | dsh plugin --profile web add openharness-rule-for-dsh-plugin |