dsh-onlyoffice
Verified@dake6767/dsh-onlyoffice · v0.1.9 · AGPL-3.0-only · Web UI
DSH web plugin: open .docx/.xlsx/.pptx from the sidebar file manager in an embedded OnlyOffice editor (static SDK, no Document Server).
Install
dsh plugin add @dake6767/dsh-onlyoffice Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-onlyoffice
English | 中文
一个 DeepSeek Harness 插件:在 DSH 侧栏的文件管理里打开 .docx / .xlsx / .pptx,用内嵌的 OnlyOffice 编辑器查看和编辑。纯前端静态 SDK,不需要 Document Server,也不需要后端转换服务。文档始终留在本机,唯一的外网流量是首次打开时下载 OnlyOffice 引擎资源(之后走本机磁盘缓存)。
| 包名 | @dake6767/dsh-onlyoffice |
| Host 半部 | lib/index.js — /dsh-onlyoffice/* 路由(SDK 代理 + 缓存、读/写、诊断) |
| Client 半部 | lib/client.js — 一个 module-loader bundle(约 570 KiB),内含引擎 |
| 编辑器引擎 | onlyoffice-web-comp 的源码(放在 vendor/onlyoffice-web-comp,有 9 处补丁,见 vendor/PATCHES.md) |
| 许可证 | AGPL-3.0(见 LICENSE、THIRD_PARTY_NOTICES.md) |
功能
- 两条集成路径,一个引擎
- DSH 内置文档预览(主路径,无第三方依赖):通过
ctx.documentPreviews+sidebar.right.tab.document槽注册实现(loading: 'renderer')。 - dsh-better-sidebar(可选):通过
ctx.betterSidebar.registerFileViewer注册三个文件查看器;对服务做了「先取 → 可选注入 → 有上限轮询」的等待,两个插件无论谁先加载都能组合成功。
- DSH 内置文档预览(主路径,无第三方依赖):通过
- 编辑后写回原文件
- 编辑器里按
⌘S/Ctrl+S(把 vendored 引擎的快捷键拦截器转接到本插件),以及编辑器自带的保存入口(onSave→ONSAVE)都会写回原文件; - 自动保存:每次文档变更会触发 1.8 秒防抖的静默写回;手动保存会立即冲刷并给出提示。
- 编辑器里按
- 外部修改不会悄悄覆盖你的编辑器:预览 watcher 发现文件版本变化时,预览顶部出现 “文件已在磁盘上更新。[重新加载]”,由你决定何时载入。
- 导出下载、只读/编辑切换、编辑器界面明暗主题跟随 DSH。
- 自诊断:打开失败时错误界面会列出捕获到的控制台/iframe 错误和环境快照,并上报到
POST /dsh-onlyoffice/diagnostic(写入<cacheDir>/.requests.log)。
环境要求
- DSH 版本需要提供
documentPreviews+slots客户端服务(即自带@deepseek-ai/dsh-client-ui-sidebar-documentpreview);可选路径需要dsh-better-sidebar。 - 首次打开文档需要能访问下文的 SDK 镜像之一。
安装
支持反向代理子路径部署:插件所有路由都按页面目录解析,因此
https://host/app/<id>/与http://127.0.0.1:53803/、桌面壳dsh-app://app/表现一致。
方式一:本地目录链接(开发/自用,当前方式)
# 在 DSH 里用 plugin_manager:
# action: install_bundle
# target: /Users/wuliqi/projects/dsh-onlyoffice
会向 profile 添加依赖并启用 bundle 行。客户端改动刷新页面(⌘R)即可;Host 半部改动需要完整重启 DSH(目录链接安装下 Host 模块的 HMR 不可靠)。
方式二:npm 包
npm i @dake6767/dsh-onlyoffice
# 或用 DSH 的插件管理器安装该包
构建与测试
node build/build-client.mjs # vendor + src/client -> lib/client.js(约 570 KiB)
node build/build-host.mjs # host/*.js -> lib/*.js
node --test "test/**/*.test.mjs" # 87 个测试
OnlyOffice 静态 SDK 从哪来
插件不打包 SDK(完整目录约 1.96 万个文件 / 1.1 GB)。Host 按需代理单个文件并缓存:
GET /dsh-onlyoffice/sdk/onlyoffice/<version>/<rest>
命中缓存 -> <cacheDir>/<version>/<rest>
未命中 -> <mirror>/onlyoffice/<version>/<rest>(按顺序取第一个可用的镜像)
默认镜像(网络错误 / 超时 / 404 / 5xx 时自动切换):
https://onlyoffice-web-comp.vercel.app/packageshttps://cdn.jsdelivr.net/gh/electroluxcode/onlyoffice-web-comp@main/public/packageshttps://raw.githubusercontent.com/electroluxcode/onlyoffice-web-comp/main/public/packages
明确 404 的资源(例如所有镜像都没有的 dictionaries/en_US/* 拼写词典)会被记住 10 分钟,避免每次渲染都重走一遍镜像链。
配置(cordis.patch.yml 的 onlyoffice 行)
- id: onlyoffice
name: '@dake6767/dsh-onlyoffice'
config:
version: '9.4.0-develop' # SDK 目录版本(客户端从 /status 读取,不写死)
upstreamBase: 'https://onlyoffice-web-comp.vercel.app/packages'
upstreamFallbacks: [] # [] = 只用单一源;省略则用上面的默认镜像链
cacheDir: '~/.dsh/onlyoffice-sdk'
fetchTimeoutMs: 300000 # 单个资源的上游超时
saveEnabled: true # false 关闭写回路由
autoSave: false # true 才启用防抖自动保存
# 手动 ⌘S / Ctrl+S、编辑器保存按钮始终可用
maxSaveBytes: 268435456 # 写回大小上限(256 MiB)
离线/内网:把同样目录结构的文件预先放进 cacheDir,或把 upstreamBase 指向自建静态站点。
多用户宿主(共享 SDK 缓存)
默认每个 profile 会把 SDK 下载到自己 $DSH_HOME/onlyoffice-sdk(首次冷启动约 100 MB)。
多用户部署建议指向同一个共享目录:
# 0.1.4+:给所有 Host 进程设置环境变量
export DSH_ONLYOFFICE_CACHE_DIR=/var/lib/dsh-phalanx/data/onlyoffice-sdk
或在插件行 config 里设置 cacheDir。优先级:显式 config > 环境变量 > 默认路径。
无法设置环境变量的宿主,也可以把每个用户的 ~/.dsh/onlyoffice-sdk 软链到共享目录。
HTTP 路由(全部经过 connection fence,与 /sidebar/* 一致)
| 路由 | 作用 |
|---|---|
GET /status |
{version, sdkBaseUrl, upstreams, cacheDir, files, bytes, lastError} |
GET /sdk/onlyoffice/<ver>/<rest> |
SDK 代理 + 磁盘缓存(immutable 响应头) |
GET /read?sessionId&path[&cwd] |
读取文档字节,返回 x-dsh-file-version(renderer 模式) |
POST /save?sessionId&path[&cwd][&mtimeMs] |
原始文档字节原子写回 |
POST /diagnostic |
浏览器诊断(限长),追加到 .requests.log |
写回安全(本插件唯一的写路径):
- 目标必须已存在且是常规文件,并且位于会话工作区内(优先
fs.contains,回退 realpath 包含判断); - 扩展名只允许
docx / xlsx / pptx(引擎原生输出格式;把 docx 字节写回.odt/.doc/.csv会损坏文件,这些格式只读 + 导出); - 可选
mtimeMs作为乐观锁:磁盘文件已变化时返回 409 conflict,而不是覆盖; - 同目录临时文件 +
rename原子替换,保留原文件权限位。
使用说明
- 右侧栏「文件」→ 点开 docx/xlsx/pptx → 自动用 OnlyOffice 打开(预览右上角可看到「OnlyOffice 文档」实现);
- 编辑后
⌘S/Ctrl+S保存;不按也行,停手约 2 秒会自动保存(静默); - 点 OnlyOffice 自带软盘同样会写回;
- 文件被外部修改(比如 Agent 写的)→ 顶部提示条 →「重新加载」查看最新内容;
- 「导出下载」把当前文档导出为本地文件;「编辑/只读」切换编辑权限。
注意:编辑器内部的“所有更改已保存”只代表它自己的内存服务状态;真正的落盘由本插件负责(⌘S、软盘、自动保存三条路径都走同一个写回逻辑)。
已知限制
排查:space 页面提示「宿主端 SDK 路由不可达」
DSH 的 space 工作区可能挂在 /app/<spaceId>/ 下。这种页面里插件路由只有在
该前缀下才可达;同一路由放到 origin 根会被 connection fence 直接拒绝
(403 Forbidden)。客户端启动时会依次探测「页面目录 → origin → 根相对」,
并复用第一个能用 JSON 回应 GET …/status 的 base。
在页面控制台执行下面这行,可以看到每个 base 的真实响应:
await window.__dshooProbe()
它会分别对 /status 和 SDK api.js 返回 HTTP 状态、content-type、耗时和响应体前缀,
因此能一眼区分 403 / text/plain(fence 拒绝)、200 / text/html(SPA 回退)和超时。
支持的格式
| 分类 | 格式 |
|---|---|
| 用 OnlyOffice 打开(内置预览 & better-sidebar) | docx doc odt rtf · xlsx xls ods csv · pptx ppt odp |
| 可写回原文件 | 仅 docx xlsx pptx(引擎原生输出;把 docx 字节写进 .odt/.doc/.csv 会损坏文件) |
| 其它格式(仍由 DSH 自带查看器处理) | 文本、Markdown、代码、图片、PDF、HTML… |
| 引擎能解析、但这份 SDK 导出用不了 | pdf/pdfa/djvu/xps(需要 pdfeditor 应用)、Visio vsdx/vssx/vstx/...(需要 visioeditor)——当前静态 SDK 只有 documenteditor/spreadsheeteditor/presentationeditor,而 api.js 把 documentType: 'pdf' 映射到 pdfeditor,所以会 404 |
| 引擎支持但本插件未注册 | txt html mht epub fb2 mobi docm dotx dotm fodt ott oform docxf xlsm xltx xltm xlsb fods ots ppsx pptm ppsm potx potm fodp otp —— 想启用就加进 NATIVE_EXTENSIONS / VIEWER_EXTS 并补 MIME,但不要开放写回 |
扩展名必须与文件真实内容一致(x2t 按扩展名选择解析器)。
PDF 有意保留给 DSH 原生查看器:它直接渲染 PDF 内嵌字形;交给 OnlyOffice 需要额外的
pdfeditor 应用并多一次转换,纯前端静态 SDK 不适合。
- 没有实时协同/字符级热更新:静态 SDK 没有协同通道,真正的共同编辑需要 Document Server / WOPI 后端。外部修改是“提示 → 你点击 → 整篇重载”;
- 写回只支持原生 OOXML(docx/xlsx/pptx),其余格式只读 + 导出;
dsh-resource://file/absolute/...这类地址没有会话信息,可查看但无法写回;- x2t 转换是页面级单例,已用锁串行化;首次打开需要下载对应 SDK 片段;
- 侧栏的刷新控件属于内置预览;本插件额外提供的唯一重载入口是“外部更新”提示条。
排错
| 现象 | 排查 |
|---|---|
| 首次打开很久 / 一直显示「加载引擎」 | 冷启动在下载 SDK;观察 <cacheDir> 的体积是否增长 |
| 「编辑器渲染超时」 | 错误详情里有控制台错误 + iframe 状态快照 |
dictionaries/en_US/* 404 |
无语义影响(上游没有拼写词典),已做负缓存 |
保存报 conflict |
文件在磁盘上变了——先重新加载再保存 |
保存报 method not allowed |
运行中的 Host 还是旧代码——完整重启 DSH |
| 插件更新后侧栏空白 | 客户端 bundle 崩溃;错误界面会显示 [console-*],同时看 .requests.log |
<cacheDir>/.requests.log 记录每次 SDK/读/写/诊断请求的状态码与耗时(2 MiB 轮转),是定位“浏览器到底发了什么请求”的最快入口。
目录结构
host/ Host 半部(只依赖 node:* 内置模块)
index.js 路由注册、SDK 代理、状态、诊断、日志
save.js /read 与 /save:工作区校验、原子写、乐观锁
sdk-cache.js 纯函数助手(路径映射、内容类型、镜像链、缓存统计)
src/client/ Client 半部(esbuild 打成单文件 bundle)
index.jsx 注册两条集成路径;装载样式/诊断/快捷键
native.jsx 内置预览的 documentPreviews 实现 + resource 地址解析
office-view.jsx OnlyOffice 视图:加载/编辑/保存/自动保存/外部更新提示
sdk.js /status、/read、/save 客户端调用;SDK 资源基址注册
autosave.js 防抖自动保存
diagnostics.js 页面/iframe/console 捕获 + 环境快照
build/ esbuild 构建(处理 x2t worker 内联)
vendor/onlyoffice-web-comp/ vendored 引擎(补丁见 vendor/PATCHES.md)
test/ node:test:Host 路由/缓存/读写 + Client bundle/渲染 smoke
fixtures/ 演示文档(docx/xlsx/pptx + live-sync 演示)
许可证与第三方
- 本包:AGPL-3.0(
LICENSE),源码中带SPDX-License-Identifier: AGPL-3.0-only; - vendored 引擎:上游同样 AGPL-3.0,其许可文本保留在
vendor/onlyoffice-web-comp/LICENSE,修改记录见vendor/PATCHES.md; - OnlyOffice SDK 资产不在包内,运行时从镜像下载;其许可情况(社区版 AGPL、Developer Edition 商业版、
x2tWASM 许可不明确)见THIRD_PARTY_NOTICES.md; - 内置 brotli 解码器保留 Apache-2.0 头;
- OnlyOffice 是 Ascensio System SIA 的商标,本项目是独立集成,与其无隶属或背书关系。