Skip to content

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 | 中文

npm version license: AGPL-3.0 CI

一个 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 注册三个文件查看器;对服务做了「先取 → 可选注入 → 有上限轮询」的等待,两个插件无论谁先加载都能组合成功。
  • 编辑后写回原文件
    • 编辑器里按 ⌘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 时自动切换):

  1. https://onlyoffice-web-comp.vercel.app/packages
  2. https://cdn.jsdelivr.net/gh/electroluxcode/onlyoffice-web-comp@main/public/packages
  3. https://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 原子替换,保留原文件权限位。

使用说明

  1. 右侧栏「文件」→ 点开 docx/xlsx/pptx → 自动用 OnlyOffice 打开(预览右上角可看到「OnlyOffice 文档」实现);
  2. 编辑后 ⌘S / Ctrl+S 保存;不按也行,停手约 2 秒会自动保存(静默);
  3. 点 OnlyOffice 自带软盘同样会写回;
  4. 文件被外部修改(比如 Agent 写的)→ 顶部提示条 →「重新加载」查看最新内容;
  5. 「导出下载」把当前文档导出为本地文件;「编辑/只读」切换编辑权限。

注意:编辑器内部的“所有更改已保存”只代表它自己的内存服务状态;真正的落盘由本插件负责(⌘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 商业版、x2t WASM 许可不明确)见 THIRD_PARTY_NOTICES.md;
  • 内置 brotli 解码器保留 Apache-2.0 头;
  • OnlyOffice 是 Ascensio System SIA 的商标,本项目是独立集成,与其无隶属或背书关系。