资料库 · dsh-library
DeepSeek Harness 的本地产出资料库插件。
把每次对话、每个项目产出的东西收进一处:文档、表格、图片、音视频、链接、附件 —— 以自带页面的样子出现在 DSH 里,可浏览、可检索、可预览、可整理,也能让助手读取并按路径引用。
目录
功能一览 · 界面 · 快速开始 · 配置 · 架构 · HTTP API · 文档直接预览 · 安全设计 · 测试 · 路线图 · 与资产库的边界 · 排障 FAQ
功能一览
| 能力 | 说明 |
|---|---|
| 🗂 三栏主页面 | 左目录树 / 中列表(网格与列表可切换)/ 右内容预览,视觉与 DSH 原生页面同一批零件 |
| 🔍 整库检索 | 按文件名、路径、标签、备注全文搜索;类型 / 目录 / 标签三组筛选 chip 可叠加,计数随口径实时变化 |
| 👁 文档直接预览 | Markdown、Excel(.xlsx / .csv / .tsv)、Word(.docx)、JSON 与代码文件在页内直接查看,不下载、不跳走 |
| 🏷 标签与备注 | 每条资料可打标签、写备注;支持批量标注(一次最多 2000 条) |
| 📥 多种入库 | 拖拽进页面、系统文件选择、新建文档/文件夹、「从目录导入」批量复制 |
| 🤝 助手可存不可改 | 助手能把新产出存进库,但不可修改 / 移动 / 删除已有资料 —— 整理权在人 |
| 🌗 明暗自适应 | 配色只读 DSH 设计令牌 --dsw-alias-*,跟随宿主主题与密度设置 |
| 🧩 零第三方依赖 | 运行时只依赖 Node 内置模块与 DSH 自身提供的平台件,xlsx/docx 解析基于 node:zlib 手写最小 zip 读取器 |
界面
三栏布局 —— 左树 / 中列表 / 右内容,点开任意一条是页内「放大卡片」:

网格视图,媒体卡片带类型角标与标签:

右栏直接预览 Markdown / Word / Excel / 代码(真实渲染,非截图拼接):
| Markdown | Word 文档 |
|---|---|
![]() |
![]() |
快速开始
前置:Node.js ≥ 22、已安装 DeepSeek Harness、本仓库已在本地。
# 1) 装进 profile(官方 CLI 负责 bundle 层)
& 'F:\DSH\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop add 'D:\dsh-library'
然后编辑 C:\Users\<你>\.dsh\profiles\desktop\package.json:
- 把依赖规格从
link:改成file:(file:让 pnpm 复制成实体目录,解析链才完整); - 把
dsh-library加进dsh.profile.bundles数组 —— 不加这一行,插件不会进组合配置树。
{
"dependencies": { "dsh-library": "file:D:/dsh-library" },
"dsh": { "profile": { "bundles": ["…", "dsh-library"] } }
}
# 2) 同步一次,然后完全重启 DSH(托盘也退出)
& 'F:\DSH\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop install
重启后侧边栏出现「资料库」入口即成功。改完源码不生效、或想核对安装状态,见排障 FAQ。
配置
cordis.patch.yml 里按行 id 覆盖(全部字段都有默认值,不配也能跑):
- id: library
name: dsh-library
config:
root: 'D:/dsh-library-data' # '' = 退回 <DSH_HOME>/library
includeHidden: false
maxDepth: 8
maxFiles: 20000
pageSize: 60
cacheMs: 1500 # 扫描缓存毫秒数;写操作会立刻作废缓存,要绝对实时就设 0
maxUploadBytes: 67108864 # 单次入库上限(64 MB)
allowAgentSave: true # 助手可把新产出存进库
allowAgentModify: false # 助手不可改动 / 移动 / 删除已有资料
库根解析顺序:显式配置 → 出厂默认(lib/config.js 的 LIBRARY_DEFAULTS.root)→ 显式 '' 退回 <DSH_HOME>/library。
出厂默认刻意写死一个非系统盘路径:库根放错会静默留在 C 盘上,等用户发现时已经积了几 GB —— 把默认值与补丁层设成同一处,堵掉这个失败模式。换机器时两处一起改。
架构
插件分两半,同包不同进程侧:
dsh-library/
├── index.js # 宿主半入口:激活、配置、路由挂载
├── lib/
│ ├── config.js # 配置默认值与解析
│ ├── scan.js # 递归扫描 + 目录汇总(唯一事实来源)
│ ├── store.js # .library-index.json(只存人的输入:标签/备注/来源)
│ ├── service.js # 内核:快照缓存、列表/搜索、文档解析、写操作
│ ├── routes.js # HTTP 面:15 个端点,先过信任栅栏再干活
│ ├── paths.js # 路径归一化与包含检查(安全核心)
│ ├── kinds.js # 类型目录(未收录扩展名归 file,不是忽略)
│ ├── names.js # 名称校验与去重(名字 (2).ext,Windows 保留名)
│ └── office.js # 零依赖 xlsx/docx 解析(自写最小 zip 读取器)
└── client.js # 浏览器半:三栏面板,经 window.__ModuleLoader__ 注入
原生感的来源 —— DSH 的界面本身由槽位组合而成,一个原生页面只有一种做法:
侧边栏条目 slots.inject('sidebar.panellist') → register({ id, order, label }, Icon)
页面本体 slots.inject('main') → register({ key: <同一个 id> }, Panel)
同一个 id 连接两者,点击侧边栏即切换页面。没有修改 DSH 安装本体(那会被应用更新覆盖)——所谓「内置」是走插件组合层,体验上与自带页面无差别。
扫描是唯一事实来源,索引只保存人的输入:
<库根>/
├── .library-index.json # 标签 / 备注 / 来源;扫描自动跳过它
├── 分组/
│ ├── 笔记.md
│ └── 2026/
│ └── 截图.png
└── 附件.zip
你在文件管理器里删掉或改名,资料库下一轮扫描就跟着变,不会留下幽灵条目;扫描默认跳过点开头条目、node_modules / .git、Office 临时文件与系统垃圾。
HTTP API
一条前缀路由承载全部端点,全部先过浏览器信任栅栏:
| 端点 | 说明 |
|---|---|
GET /api/library/status |
库根、条目统计、能力清单(面板按能力禁用控件,不报错) |
GET /api/library/items |
列目录或搜索。scope / parent / q / kind / tag / sort / dir / limit / offset / recent |
GET /api/library/item?path= |
单条详情(含绝对路径,供助手引用) |
GET /api/library/tree |
分组树 + 最近添加 |
GET /api/library/facets?scope= |
chip 行计数 —— 按范围统计而非按当前页,翻页数字不会漂 |
GET /api/library/document?path= |
文档内容解析,四类出口见下表 |
GET /api/library/file?path= |
文件流。Range / If-None-Match / HEAD / download=1 全支持 |
POST /api/library/mkdir /create /upload |
入库三件套,只新增不覆盖(重名自动 名字 (2).ext) |
GET /api/library/external?dir= |
库外目录候选清单(只读扫描) |
POST /api/library/import |
从库外目录成批复制进来(只复制不移动,逐份报错不整体失败) |
POST /api/library/annotate |
标签 / 备注 / 来源,单条与批量同端点 |
POST /api/library/rescan |
强制重扫 |
POST /api/library/reveal |
系统文件管理器定位资料。唯一离开进程的端点,受 allowReveal 开关控制 |
scope用「键是否存在」区分模式:出现了scope键 = 检索模式(递归找文件),没出现 = 子项模式(只列直接子项)。子树比较用scope + '/'前缀而不是裸startsWith,否则图片集/里的东西会被算进图片/。/facets不能由面板自己数:面板手里只有当前一页,自数的计数会随翻页变化。chip 说的是「这个范围里有多少」,不是「这一页里有多少」。/annotate批量契约:请求给路径paths,响应回条目items+missing(已不存在、没改成的路径),面板据此报「已更新 5 项,有 1 份没改成」。命中为空时不碰磁盘,避免造出空条目。/reveal三道收口:配置开关(关掉时按钮整个不出现)→resolveInside挡路径逃逸 → 隐藏段 403;再stat确认存在才 spawn,且只 spawn 不经 shell——shell: true等于把文件名当命令行片段解释。
文档直接预览
/document 按扩展名把文件解成结构化内容,右栏直接查看:
| 扩展名 | 返回 | 渲染 |
|---|---|---|
.md |
{ text } |
官方 MarkdownText |
.xlsx / .csv / .tsv |
{ sheets: [{ name, rows }] } |
页内表格(多工作表可切) |
.docx |
{ blocks: [...] } |
heading / list / paragraph / table 按文档顺序 |
.json 与代码 |
{ text } |
官方 CodeBlock(语法高亮) |
失败按 code 分级而不是合成一句「打不开」,因为处置方式完全不同:
ENCRYPTED(422) → 去密码另存;NOT_XLSX / CORRUPT 等(422) → 重新导出;UNSUPPORTED(415) → 换格式;TOO_LARGE(413) → 下载。
三条实现约束:CSV 用状态机解析(引号内换行/逗号、"" 转义,split('\n') 三种全错);文本上限 512 KB 只读前 N 字节,截断是正常结果(truncated: true + 界面一行小字)而非错误态;语义失败统一 4xx 而不是 5xx——5xx 的运维口径会指向「资料库自己坏了」。
安全设计
所有能从库根读到字节的路径都过同一套收口,缺一不可:
- 路径包含检查(
resolveInside):归一化后必须仍落在库根内,..、盘符、绝对路径一律拒绝。Windows 上大小写不敏感 + 给根补分隔符(否则C:\lib2会被C:\lib判成在内);逃逸校验在解码之后(%2E%2E还原成..也要拦住)。 - 隐藏段拒绝:任何一段以点开头直接 403 —— 索引文件与库根元数据不在可读范围。
- HTML / SVG 强制
CSP: sandbox:能携带脚本的格式在流式返回时加沙箱头。少了它,「在资料库里预览一个页面」就等于在 DSH 页面所属的源里执行别人写的脚本。 - 信任栅栏按请求解析:裸 Web 路由不继承 Connection 服务的 Host/Origin 检查,每个请求先过栅栏再做任何工作(Connection 行可能在路由注册之后才激活,快照式捕获会留永久空洞)。
- 助手权限收窄:
allowAgentSave只开「往里放新东西」;整理、改名、删除一律由人在页面上做。
入库四端点只新增不覆盖;写入先落 .part 临时文件再改名(.part 本就在扫描跳过规则里,写一半失败不会留下半截文件);批量导入只认普通文件(lstat,符号链接跳过)。
测试
五套离线测试、500+ 项断言,不启动 DSH 也能跑:
npm test # logic / scan / client / host / office 五套
npm run check # 只读诊断真实库根
| 套件 | 覆盖 |
|---|---|
logic |
内核纯逻辑:路径、名称、类型 |
scan |
扫描、目录汇总、跳过规则、截断 |
client |
面板交互:迷你渲染器按依赖数组真实跑 effect,导航/筛选/拖拽/批量都过一遍 |
host |
HTTP 面:真实起 http 服务器走真 socket(假 response 验不了 Range 与流) |
office |
xlsx/docx 解析:正常、损坏、加密、格式错配、截断 |
- 样式表必须渲染进元素树(
h('style', null, STYLE))。宿主不会顺手执行你写在模块作用域里的模板字符串——CSS 只在元素树里出现浏览器才建样式表,否则真机上是无样式的裸 HTML,而 className 断言全绿。契约测试有四条断言守着这件事。 file:装法是副本不是软链,pnpm 不按内容哈希检测目录依赖变更——改完源码必须手动同步,否则「磁盘上全对、界面上没变化」。- 离线替身测不出真机契约错配(例:宿主 locale 只认字符串模板词条)。给面板包一层 class 错误边界、
componentDidCatch把堆栈渲染成可见文本,真机一次就拿到根因。
路线图
| 阶段 | 内容 | 状态 |
|---|---|---|
| 1–3 | 插件骨架、宿主半(库根/扫描/索引/HTTP)、页面与入库 | ✅ 完成 |
| 5 | 页内放大卡片、视觉重做、/facets、批量标注、/reveal |
✅ 完成 |
| 6 | 离屏渲染 + 无头截图验证链(README 截图即产自这条链) | ✅ 完成 |
| 7 | 三栏布局 + 四类文档直接预览、零依赖 Office 解析 | ✅ 完成 |
| 8 | 助手工具:概览 / 检索 / 取用 / 保存 | ⏳ 待办 |
| 9 | 进阶:合集(一组资料当一个整体检索)、参照图钉 | ⏳ 待办 |
| — | 从交付文件卡片一键收进资料库 | ⏳ 需先还原会话记录里的真实路径,单独评估 |
与资产库的边界
dsh-asset-library(项目素材索引)与本插件(跨会话资料库)独立并存、互不依赖、不互相替代:
| 资产库 | 资料库(本插件) | |
|---|---|---|
| 管什么 | 项目目录里的素材 | 对话与项目产出的资料 |
| 文件在哪 | 原地不动,只索引 | 收进中央实体库,是真实副本 |
| 语义 | 浏览与筛选 | 归档与复用 |
资料库是跨会话的中央库,刻意不注册任何右侧栏停靠面——从「一次会话的侧栏」打开跨会话的库,坐标系不对。契约测试有一条断言守着「不注册右侧栏 tab」别悄悄回来。
排障 FAQ
改完源码为什么不生效?file: 装的是副本(profiles\desktop\node_modules\dsh-library),且 pnpm 不按内容哈希检测目录依赖变更——dsh plugin install 会回 "Already up to date" 但什么都不拷。每次改完源码跑一次同步:
powershell -NoProfile -ExecutionPolicy Bypass -File "D:\dsh-library\tools\sync-all.ps1"
宿主半与面板都需要完全重启 DSH。客户端模块是进程启动时注入的,刷新窗口或重开对话都不会重新加载它。托盘图标还在就是没退干净。
想核对安装状态但不启动 DSH?node tools/install-check.mjs # 只读,26 项
它查磁盘状态:profile 声明、lock 条目、文件是否同步、插件自身声明、库根位置、两个注册席位。它查不了「DSH 进程里加载没加载」——/api/library/* 是进程内虚拟路由,不监听 TCP 端口,curl 不出来,最后一步只能重启后在界面上确认。
按顺序查:① package.json 的 dsh.profile.bundles 数组里有没有 dsh-library(没有就永远不会挂载);② 是否完全重启过 DSH;③ node tools/install-check.mjs 看文件是否同步齐。参见同系列 dsh-plugin-development 经验:入口消失最常见的原因是 bundles 缺条目与没退干净。


No comments yet. Be the first to write one.