Skip to content
dsh.fish
Bundle

openharness-reader

OpenHarness Reader — workspace file browser/editor with Markdown preview for DeepSeek Harness Web

Source
zuoguyoupan2023
License
MIT
Updated
Updated 3 days ago

Readme

# 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](https://github.com/zuoguyoupan2023/openharness/releases/tag/v0.1.0)) | ✅(v0.4.3) |
| v0.4.4 | **适配 DSH 0.1.1-rc.2**:peer 范围放宽为 `^0.1.0-rc.6 \|\| ^0.1.1-rc.2` 双线覆盖,devDeps 升至 0.1.1-rc.2 并全量校验(typecheck / build / test 全绿);调研见 [`000-dsh-011rc2.md`](000-dsh-011rc2.md) | ✅(v0.4.4) |
| v0.4.5 | **品牌位**:按官方契约占用 `sidebar.brand.mark` / `sidebar.brand.name`(dsh 0.1.1+;rc7 自动 no-op),OpenHarness 统一品牌壳上侧边栏品牌行;槽位被占时自动让位 | ✅(v0.4.5) |
| v0.4.6 | **启动行为选项**:设置卡片新增「默认展开 / 默认折叠」(localStorage `prefs.autoOpen`,默认展开=历史行为;折叠后新会话/新项目不再主动展开,点 Reader 竖条手动开);**品牌位负优先级 shadow**:以 `priority:-100` 压过先到的 priority-0 注册者(single 槽 lowest-renders 语义,修复实测冲突) | ✅(v0.4.6) |
| M5 | npm 发布 + 壳 `AUTO_INSTALL_PLUGINS` 预装 | ⏳ |

## 功能(v0.4.1 · 样式参考 dsh-better-sidebar)

> **样式参考**:本插件的布局与视觉**参考了 [dsh-better-sidebar](https://github.com/omdsh-dev/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`,以读取时版本做 `replaceIfVersion` CAS —— 磁盘文件被外部(如 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`](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.item` keyed 卡片);改完保存即持久化到 profile,刷新仍在。卡片自带 revision 设栅——并发修改会冲突重读而非静默覆盖。
- **品牌位(OpenHarness 统一品牌壳)**:按官方 `sidebar.brand.*` 槽位契约(`dsh-client-ui-sidebar@0.1.1-rc.2` 白纸黑字邀请部署包替换鱼形 fallback),在侧边栏品牌行渲染「双括号托举节点」mark(展开行 / 收起轨道自适应壳给的尺寸)+ 「Open**Harness**」双色词标。**仅 0.1.1+ 生效**——rc7 无此槽位声明,声明感知注入整体 no-op、官方鱼标原样保留;以负优先级 shadow 先到的占位注册(single 槽 lowest-renders 语义),同优先级冲突仍自动让位。
- **策略一致**: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`,authority `loopback`,与 `/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-reader` settings 命名空间(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-width` CSS 变量推挤 `#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.6**。

### 方式一:App 插件中心(推荐,无需命令行)

1. 下载并安装 DSH 桌面 App([GitHub Releases · openharness](https://github.com/zuoguyoupan2023/openharness/releases/tag/v0.1.0),下载对应系统安装包),打开后在**左侧边栏**进入「**插件中心**」;
2. 在「**特别推荐**」里找到 **openharness-reader**;
3. 点击「**安装**」,等待安装完成——DSH 会**自动重启并应用**,无需手动操作;
4. 重启后即可在 Web 右侧看到「Reader」文件浏览/编辑面板(见 §功能)。

### 方式二:命令行安装

```bash
# 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 才生效**
>
> 1. 在正在运行的 DSH Web 终端按 `Ctrl+C` 停止当前进程;
> 2. 重新启动:`npx @deepseek-ai/dsh web`
>    (若机器已把 `dsh` 装进 PATH,`dsh web` / `dsh --profile web` 与之等价,任选其一)
> 3. 等 Web 界面重新打开后**刷新浏览器页面**。
>
> 不重启的话,正在运行的 DSH 进程的 boot 图里没有本插件,刷新页面也不会加载它。

`npx @deepseek-ai/dsh plugin add` 会:
1. 在 `~/.dsh/profiles/web` 里 `pnpm add` 本包;
2. **自动 reconcile**:检测到 `dsh.bundle` 声明,把 `openharness-reader` 追加进 `dsh.profile.bundles`,成为 profile 补丁层。

### 升级 / 卸载

```bash
# 升级到最新版(当前 0.4.6)
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 || ^0.1.1-rc.2` 同时覆盖 0.1.0-rc.6 / rc.7 与 0.1.1-rc.2
> 两条版本线,安装零警告、不强制升级。已对 `0.1.1-rc.2` 完成依赖面逐包比对与
> 回归验证(typecheck / build / test 全绿),调研详情见 [`000-dsh-011rc2.md`](000-dsh-011rc2.md)。

### 本地开发安装(file: 符号链接,改代码无需重装)

```bash
# 在插件仓库目录或其父目录执行(file: 相对路径以调用目录为锚)
npx @deepseek-ai/dsh plugin --profile web add file:../openharness-reader
```

file: 依赖是符号链接:客户端改动 `npm run build` 后**刷新页面**即可(DSH Web 无 HMR);patch / bundles 改动需重启 profile。安装后同样需要按上面的提醒重启一次才生效。

### 验证

```bash
npx @deepseek-ai/dsh --profile web --dump-config | grep -A3 openharness-reader   # 合成树里应有插件行
# 浏览器 DevTools → Network 应能看到 /plugins/openharness-reader/client.js 正常返回
```

## 开发

```bash
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](https://github.com/zuoguyoupan2023/adhdgofly-dsh-ext) | `dsh plugin --profile web add adhdgofly-dsh-ext` |
| **openharness-reader** | 工作区文件浏览/编辑 + Markdown 预览 | [zuoguyoupan2023/openharness-reader](https://github.com/zuoguyoupan2023/openharness-reader) | `dsh plugin --profile web add openharness-reader` |
| **openharness-reply-in-cn** | 强制模型用简体中文回复,侧边栏「中文回复」项 | [zuoguyoupan2023/openharness-reply-in-cn](https://github.com/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](https://github.com/zuoguyoupan2023/openharness-rule-for-dsh-plugin) | `dsh plugin --profile web add openharness-rule-for-dsh-plugin` |

Install

dsh plugin --profile web add github:zuoguyoupan2023/openharness-reader

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source