hiwork-office
v0.0.4
Published
HiWork 文档办公插件:集成 OnlyOffice Document Server,支持工作区文档(docx/xlsx/pptx 等)的在线预览与编辑,编辑结果自动回写工作区文件。
Downloads
193
Readme
hiwork-office
HiWork 的文档办公插件:把 OnlyOffice Document Server 集成进桌面端,让 DSH 生成的工作区文档(docx / xlsx / pptx 等)可以直接在线预览与编辑,编辑结果由 Document Server 回调并自动回写工作区文件。
两种打开方式,互补而不是二选一:
- 在线预览 / 编辑:文档嵌在 HiWork 里(中央页 + 右侧栏 tab),无需本机装 Office; 文档怎么到 DS 手里有两条通道,由管理员的网关开关决定(见「在线编辑的两条通道」);
- 本地预览:交给本机默认应用打开(Word / Excel / WPS / Keynote…),不经过
Document Server、不需要登录,Agent 也能调(工具
office_open_local)。
并且:在线编辑中的文档,Agent 也能读写——见「Agent 桥」(工具 office_document_*),
它需要把三个插件文件装到 Document Server 上(一次性运维步骤,README 里有一行命令)。
- 包名 / bundle id:
hiwork-office - client 插件名:
hiwork-office-client - 中央页 feature:
id = office,order = 50(排在定时任务 20、知识库 30、能力中心 40 之后) - Agent 工具:
office_open_local(把工作区文档推到用户眼前)、office_document_outline/office_document_read/office_document_read_format/office_document_list_styles(读在线文档)、office_document_insert_text/office_document_set_cells/office_document_format_text/office_document_format_paragraph/office_document_apply_style/office_document_replace_text/office_document_insert_table/office_document_format_cells等(写在线文档,带人工确认门)
工作原理
┌────────────────────┐ ① GET /doc/<token>/文件名 ┌───────────────────────────┐
│ hiwork-desktop │ ◀────────────────────────────── │ OnlyOffice Document Server │
│ ┌──────────────┐ │ (Document Server 拉文档) │ https://office-suite. │
│ │ DocsAPI │ │ │ hivery.cn │
│ │ 编辑器 iframe │──┼──② 加载 api.js + 提交 config ──▶ │ (remote-dev-96 Docker, │
│ └──────────────┘ │ │ 经反代出 HTTPS 域名) │
│ ┌──────────────┐ │ ④ POST /callback/<token> └────────────┬──────────────┘
│ │ hiwork-office │◀─┼──────────────────────────────────────────────┘
│ │ Host 半边 │ │ (编辑保存回调:从 body.url 下载并覆盖本地文件)
│ │ · 文档扫描 │ │
│ │ · JWT 签发 │ │ ③ RPC /hiwork-office(config / files / session)
│ │ · 文档服务 │◀─┼────────────────────── Web 半边(文件列表 + 编辑器挂载)
│ └──────────────┘ │
└────────────────────┘- 打开文档:Web 半边发
session.open { path, mode };Host 校验路径在工作区内、 按文件内容算document.key(sha256 截断)、登记随机 token,组装完整 DocsAPI config 并以 HS256 签名(JWT_IN_BODY约定)返回。 - 渲染:client 动态加载
${serverUrl}/web-apps/apps/api/documents/api.js,new DocsAPI.DocEditor(el, config)挂进中央内容区。 - 拉取:Document Server 从本机文档服务
GET /doc/<token>/文件名拉文档 (部署侧已开ALLOW_PRIVATE_IP_ADDRESS,私网地址可达;本机地址自动探测私网 IPv4,也可在 bundle config 里显式指定advertiseHost)。 - 保存:用户保存 / 关闭编辑器时 Document Server 回调
POST /callback/<token>(带 JWT,Host 验签),Host 从回调 body 的url下载最新文档并原子覆盖本地 文件,回{"error":0};文件内容变了,下次打开的document.key自动变化, Document Server 侧不会复用旧缓存。
配置与凭据来源
凭据优先级:网关下发(managed)> 本地覆盖(manual)> bundle 默认。
| 配置 | 主要来源 | 备用来源 | 说明 |
| --- | --- | --- | --- |
| Document Server 地址 | 网关下发(GET /api/me/office-config) | 设置界面 / cordis.patch.yml | 网关下发经 hiwork-core 的登录态自动取得,终端零配置 |
| JWT 密钥 | 网关下发(同上) | 设置界面(离线备用) | 网关不可用时才用本地值;密钥永不下发到 Web 半边、不进 npm 包 |
| 监听端口 | cordis.patch.yml | — | 本机文档服务端口,0 = 系统分配 |
| 通告地址 | cordis.patch.yml | — | 留空自动探测私网 IPv4;Document Server 必须能回连它 |
网关下发怎么走(2026-09-16 起,替代「每台终端手工配密钥」):
hiwork-office Host ──① ctx.get('hiworkOfficeCredentials') ──▶ hiwork-core Host
│ ② 用网关登录 cookie 拉
▼
GET https://aigateway.hivery.cn/api/me/office-config
(网关集中持有共享密钥,配置见 ai-gateway config.yaml 的 office 段)- 站点侧只需在一处配置密钥(网关
config.yaml的office.jwt_secret),轮换改这一处; - 终端侧:登录企业网关后自动取得,无需任何手工配置;下发值缓存在 Host 内存 (TTL 30 分钟,打开文档 / 刷新探测 / 手动同步都会按 TTL 补齐),失败保留上一次成功值;
- 未安装 hiwork-core(独立安装)或网关未配置时,回退到本地设置里的地址与密钥;
- 设置界面显示「密钥来源」(网关下发 / 本地配置 / 未获取)+ 更新时间,并提供 「从网关同步」按钮。
首次使用(已登录网关、网关已配置 office 段):打开「文档」feature 即可用——徽标变绿。 独立安装 / 离线:在「OnlyOffice 服务器设置」里填密钥(部署速查里的值)作为备用。
入口分工(2026-09-17 起:预览 / 编辑默认在右侧栏)
| 入口 | 落地形态 | 说明 | | --- | --- | --- | | 「文档」页点预览 / 编辑 | 当前会话的右侧栏 tab | 与对话并存;右栏自带展开 / 浮动 / 分栏控制就是"看大图"的路径;Agent 桥随之在该 tab 里生效(写工具能驱动它) | | 聊天里的文档链接 | 右侧栏 tab | 与上同——同一份文件共用同一个 document key | | 「文档」页点本地 | 本机默认应用 | 不经过 Document Server、不需要密钥(无头环境禁用) |
中央区没有编辑器、也没有说明位(2026-09-17 起):「文档」页就是一个文件列表(独占整页宽度), 编辑器只活在右栏 tab;同一份文件在右栏打开时,列表里那一行会高亮——这就是"哪份在被编辑"的唯一提示。 「中继同步状态」「通道 / 模式」「AI 可操作」三个徽标都在右栏那一行的上方(编辑器在哪,状态就在哪)。
右栏导航用的是当前 DSH 的根级 ctx.sidebarRight.openTab(kind, {params}):它落在当前显示的
会话的右栏并自动展开,所以中央页不需要会话 id。宿主没有这个面(独立安装 / 旧宿主 / 当前不在
会话里)时,插件静默回落到中央区内嵌编辑器。
目录
src/
index.ts Host 入口:OfficeService + RPC + 文档服务 + Agent 工具的生命周期
service.ts OfficeService:凭据来源解析(网关下发 > 本地 > bundle)、工作区扫描、会话签发、本地预览、Agent 桥会话
bridge.ts Agent 桥的线协议(postMessage 形状、op 白名单、编辑器 config 注入块)
bridge-host.ts 桥的 Host 运行时:在线会话表 + 命令队列 + 在途命令 + 超时
docserve.ts 本机文档服务:/doc 下发 + /callback 保存回写(验签)
local-open.ts 「本地预览」打开器:复用 @deepseek-ai/dsh-native-command(跨平台、shell-free)
tools.ts Agent 工具:office_open_local(本地预览)+ office_document_*(Agent 桥,
声明表驱动:加能力 = 插件一个 case + 表一行)
jwt.ts HS256 JWT(node:crypto 手写,host 产物自包含)
document-types.ts 扩展名白名单 → documentType / MIME
protocol.ts /hiwork-office RPC 契约(endpoint + zod schema + 错误码)
rpc.ts RPC Host 实现(loopback authority)
client/
index.ts Web 入口:注册 feature / 设置页降级
OfficeView.tsx 文档页:左文件栏(预览 / 本地 / 编辑)+ 右编辑器 + 设置弹层
OfficeSidebarTab.tsx 右侧栏 tab 正文(同一编辑器的第二种入口)
editor.tsx api.js 加载器 + DocsAPI.DocEditor 生命周期封装
bridge.ts 客户端桥:插件握手(event.source 绑定)+ 命令长轮询 + 结果回传
runtime.ts RPC 动作集(信封解包 + 哨兵错误)
locales.ts 命名空间 hiwork-office 的 zh/en 词典
contracts.ts 宿主 client 能力的结构契约(type-only)
styles.ts / styles.css 样式注入
assets/
agent-bridge/ **DS 侧部署件**:插件三件套(config.json / plugin.js / index.html)
scripts/
host-build.mjs Host 产物自包含打包(esbuild bundle;官方包一并内联)+ 插件资产拷进 lib/
client-build.mjs Client 产物闭包工厂打包(rc.1 契约)
tests/ JWT / 协议 / 文档服务 / 本地打开 / 工具 / 服务 / RPC / 视图 / 打包门禁在线编辑的两条通道(中继 / 本机)
在线编辑要求 Document Server 主动回连文档 URL。两条通道的差别就是"那个 URL 在哪":
| | 云端中继(relay) | 本机(local) |
| --- | --- | --- |
| 文档 URL 在哪 | 网关中继服务(DS 能访问的服务器侧) | 用户电脑上的本机文档服务 |
| 对用户网络的要求 | 无——只要能上网就能编辑(家、出差、VPN 均可) | DS 必须能连到用户电脑(同局域网 / 双向可达) |
| 谁决定用哪条 | 管理员:网关 office.relay_enabled | 中继不可用时的自动回落 |
| 文档去向 | 经服务器短时中转(TTL 12h、关闭即删、只留最新一版) | 不出本机 |
实现要点(设计见 ai-gateway 的 docs/superpowers/specs/2026-09-16-office-document-relay-design.md):
- 打开文档:读本地文件 → 上传中继 → 编辑器 config 指向中继签发的两条签名 URL (DS 拉取 / 回调),本地不暴露任何端口;
- 编辑期间:每 3s 轮询中继版本号,有新版本就取回并原子写回工作区文件;
- 冲突保护:若编辑期间本地文件被外部改动,编辑器版本写成
<名字> (HiWork 编辑冲突 <时间>).<ext>副本而不是覆盖; - 编辑器栏显示通道徽标(云端中继 / 本机)与同步状态行(已同步到第 N 版 / 冲突 / 已过期);
- 失败不静默:上传失败、会话过期都会在页面给出文案与原因。
document key 掺入打开模式:DS 按 document.key 复用编辑会话,key 只由内容 hash 构成时
「先预览、再编辑」会接到那个只读会话(编辑器不能输入、写命令超时)。因此本地会话的 key 是
内容 hash + 模式,中继会话的 key 由网关按 内容 hash + 模式 生成,view 与 edit 永不共用会话。
一份文件 = 一份在线文档(联动的前提):同一路径 + 同一模式的会话是共享的——
中央页与右侧栏打开同一份文件时,拿到的是同一个 document.key(中继侧也只上传一次),
DS 因此把它们当同一个文档协同:任一处的改动(包括 Agent 的写入)实时出现在另一处。
引用计数管理生命周期:还有视图在看就不拆会话,最后一个视图关掉才停轮询并通知中继清理。
(view 与 edit 不共享:权限不同,在 DS 侧本来就是两份 config。)
Agent 桥(让 Agent 读写在线编辑中的文档)
在 HiWork 里用「编辑」打开的文档,Agent 也能读、也能改(改动同样由编辑器保存链路 回写工作区文件)。十八个工具,按"读现状 → 改 → 读回"分三组:
| 工具 | 类型 | 作用 |
| --- | --- | --- |
| office_document_outline | 读 | 文档结构(Word 段落数 + 前几段;表格工作表名 + 已用区域) |
| office_document_read | 读 | 按段落区间 / 单元格区域读内容(超 64KB 截断并标 truncated) |
| office_document_read_format | 读 | 读某一处的当前格式(字符 + 段落 + 具名样式),改之前先看现状 |
| office_document_list_styles | 读 | 列出文档里可用的具名样式(套样式前先确认名字怎么写) |
| office_document_insert_text | 写 | 向 Word 文末追加一个段落 |
| office_document_format_text | 写 | 字符格式:加粗/倾斜/下划线/删除线/字色/字体/字号/大小写/上下标/字间距 |
| office_document_format_paragraph | 写 | 段落格式:对齐/缩进/段前后距/行距/段中不分页/与下段同页/段前分页/大纲级别 |
| office_document_apply_style | 写 | 套具名样式(「标题 1」「正文」…)——做标题层级与目录结构用这个 |
| office_document_replace_text | 写 | 全文查找替换(回报改前命中几处、改完还剩几处) |
| office_document_insert_table | 写 | 在文末插入表格并可填内容 |
| office_document_format_table | 写 | 调已有表格的样式 / 水平对齐 |
| office_document_set_cells | 写 | 写表格单元格(values 是二维数组的 JSON) |
| office_document_format_cells | 写 | 单元格格式:字体/底色/边框/数字格式/对齐/自动换行 |
| office_document_merge_cells | 写 | 合并 / 取消合并单元格 |
| office_document_conditional_format | 写 | 条件格式:数据条/色阶/图标集/前 N 项/高于平均/唯一值/单元格规则 |
| office_document_data_validation | 写 | 数据校验(限制能填什么,常用于下拉列表) |
| office_document_insert_pivot | 写 | 插入透视表(行/列/筛选/数值字段) |
Word 侧"改哪儿"有四种目标(format_text / format_paragraph / apply_style /
read_format 共用):selection(用户当前选中处)、search(按文本找)、
paragraphs(from_index + count,必须给 from_index)、all(全文)。
三个改格式的工具把 target 设成必填——不给就等于"改哪儿都行",那是另一回事。
长度/间距单位是缇(twip):1 磅 = 20 缇,1 厘米 ≈ 567 缇(首行缩进 2 字符 ≈ 480 缇)。
read_format 读回来的也是缇,可以直接照抄回 format_paragraph。
写工具走人工确认门(与 hiwork-automation 同构,tools/pre-execute 升级成 ask),
确认卡片会把完整改动摊开(文档 + 操作对象 + 要改成什么);读工具不审批,但编辑器栏会显示
「AI 可操作」徽标,让用户对"Agent 能看/能改这份文档"有明确预期。
能力边界:这套 op 全部落在官方 Office API 里——callCommand 的沙箱边界恰好就是它
(2026-09-17 对着 DS 上部署的 9.4.0 核实:Word 140 个 Api.* 工厂 / 1505 个类方法,
表格 168 / 2404)。仍然没有"执行任意脚本"的通道,也没有把 callCommand 透传给模型。
调研过程、能力清单与后续可扩展方向见
docs/superpowers/specs/2026-09-17-office-api-surface-research.md。
右栏是 AI 协作的主场:在聊天里点一份 docx/xlsx,右栏 tab 直接打开编辑器,顶部状态条
显示桥的状态——「AI 可操作」→「AI 正在写入…」(琥珀呼吸)→「AI 已更新」(绿,8 秒后回落)。
AI 的改动同时经 DS 协同实时出现在中央页的同名编辑器里(同一个 document.key);
中央页的文件列表也会在 AI 写完后再刷一次(中继写回有几秒延迟,会补刷第二次)。
已知限制:插件的 ready 只带文档标题(DS 不给插件传 key),因此两个同名不同目录的
文件同时开着编辑器时,桥可能把命令认到另一份上;工具侧仍要求工作区内的绝对路径。
日常(一次只开一份同名文件)不受影响。
安装(一次性运维步骤,必需)
桥的插件是 Document Server 侧的部署件:必须与编辑器 iframe 同源才会被加载,
指向用户电脑的地址(127.0.0.1 / 私网 IP)时浏览器会拦掉,插件根本不启动
(2026-09-17 spike 实测,见 docs/superpowers/specs/2026-09-16-office-agent-bridge-design.md §8.1)。
# 插件资产随 npm 包分发:node_modules/hiwork-office/lib/agent-bridge/
# 装到 DS 容器(三个文件都要,index.html 不能少);目录名 = hiwork-bridge-<资产版本>
docker cp node_modules/hiwork-office/lib/agent-bridge/. \
onlyoffice-documentserver:/var/www/onlyoffice/documentserver/sdkjs-plugins/hiwork-bridge-0.3.1/
docker exec -w /var/www/onlyoffice/documentserver/sdkjs-plugins/hiwork-bridge-0.3.1 onlyoffice-documentserver \
sh -c 'gzip -kf *.json *.js *.html; chown -R ds:ds .; chmod 644 *'目录名必须带资产版本:DS 给 sdkjs-plugins/** 下发 Cache-Control: max-age=31536000, immutable
(实测),插件窗口的 index.html 也被强缓存——只换文件内容、不换 URL,浏览器会一直用旧插件。
发布插件时把 assets/agent-bridge/config.json 的 version 与 src/bridge.ts 的
BRIDGE_PLUGIN_DIR 一起改(tests/packaging.spec.ts 会卡住漂移),然后清掉旧目录、装新目录。
协议有变化时同理换目录名——DS 还会按文档 key 缓存会话配置,同名目录容易被旧资产掩盖。
通道(没有网络面)
编辑器插件 ──postMessage──▶ 宿主页面(本插件 client) ──loopback RPC──▶ Host(OfficeService)
▲ 只认 Document Server 源 │ 命令队列 / 在途命令
└──────── postMessage(定向 event.source)◀── 长轮询 bridge.pull ─────┘- 插件不访问任何网络端点:无监听端口、无 URL 里的凭据、无 CORS/PNA 头要维护——
上下行都走 postMessage(
event.source定向发送,跨域成立); - Host 推不动客户端(DSH 的
connection.rpc在 Host 侧只有handle),所以命令靠客户端 长轮询取走(无命令时挂起 ≤25s,命令入队即刻唤醒); - 命令白名单就是上面那张表(
src/bridge.ts的OFFICE_BRIDGE_OPS,17 个 op), 没有"执行任意脚本"的通道;写操作在插件里改 + 独立读回(插入后回段落数、 写单元格后回读区域、改格式后回读属性),不信任静默失败。 读回那一趟绝不能再改一次:建表、加条件格式这类非幂等操作在插件里由whenApplying()闸住(写这段逻辑时踩过:工具报"成功"而用户看到两张表); - 回读文本先归一化再比较:
ApiParagraph.GetText()返回的是结构化文本,段尾那个\r\n是段落标记自身(不是内容),拿它跟调用方传进来的纯文本做等值判断永远不成立 (2026-09-17 反馈 #6:insert_text 明明写进去了却恒定报失败)。归一化只去尾部换行, 不能用trim()—— 有意保留的首尾空格是内容。文本类判据还要配一条"段数确实增加": 只查"文字存在"时,文档里本来就有同样一段的话,写入被吞掉也会报成功; - 会话关闭 / 组件卸载 → 在途命令立刻以"编辑器已关闭"结束,不用等超时。
只读(预览)会话:预览模式不注入插件,Agent 的工具也会立刻给出针对性提示 (写操作:"文档当前以预览(只读)方式打开,无法写入…"),不会拖到 30 秒超时。要 AI 读写, 请用「编辑」打开。
写入后自动保存:DS 即使配了 autosave: true 也不会定时写存储(保存只发生在用户关闭
文档或有人请求强制保存时),所以 AI 写完会由 Host 向 DS 的 CommandService 发一次
forcesave(去抖 1.5s、失败只记日志)——工作区文件因此不需要用户手动保存就更新。
排障:编辑器栏显示「AI 可操作」= 插件已握手;显示「AI 暂不可用」= 插件没起来
(多半是上面那步没做,或 DS 侧目录名/index.html 不对)。Host 日志会打出资产目录。
升级插件时注意两层缓存:DS 静态资源会被浏览器缓存,所以 index.html 里的 plugin.js
带版本查询串(改版本号即可);协议有变化时换目录名(hiwork-bridge-v2)并同步改
src/bridge.ts 的 BRIDGE_PLUGIN_DIR——DS 还会按文档 key 缓存会话配置。
本地预览(Agent 也能调)
文件列表每行有「预览 / 本地 / 编辑」三个动作;本地用本机默认应用打开,与在线 预览是两条独立路径:
| | 在线预览 / 编辑 | 本地预览 |
| --- | --- | --- |
| 打开方式 | HiWork 内嵌 OnlyOffice | 本机默认应用(Word / WPS / …) |
| 需要 Document Server | 是 | 否 |
| 需要登录 / 密钥 | 是 | 否(离线可用) |
| Agent 可用 | 是(Agent 桥,office_document_*;仅 edit 模式,需 DS 侧装插件) | 是(工具 office_open_local) |
实现复用 DSH 的 @deepseek-ai/dsh-native-command(macOS open / Windows
PowerShell Invoke-Item / Linux xdg-open,WSL 自动转换路径;全部 execFile
无 shell 解释),host 产物由 esbuild 内联。files.list 会回 localOpenAvailable,
无头环境(容器 / 无显示服务器)里按钮禁用并说明原因。
门禁与在线预览一致:只接受工作区内、白名单扩展名的文档。白名单在这条路径上是
安全边界而非能力边界——open 会连「可执行关联」一起执行(macOS 的 .command、
Windows 的 .bat),只放行文档类型才没有「让 agent 顺手执行仓库脚本」的口子。
安全模型
- JWT 密钥只在 Host 内存(网关下发路径)与用户本机 storageDomain(本地备用路径), 不进 npm 包、不下发 Web;网关会话 cookie 全程留在 hiwork-core 的 Host 半边;
- 文档 URL 与回调 URL 都以不可猜测的随机 token(32 hex)为路径段,文档服务 本身不接受任意路径;未知 token 一律 404;
session.open做工作区包含校验(realpath 双侧对齐,防..与符号链接逃逸), 工作区外的文件打不开;- 保存回调验签(
JWT_IN_BODY),伪造回调无法覆盖文件;覆盖先写临时文件再 rename; - 本地预览与在线预览同一套门禁(工作区包含校验 + 扩展名白名单),且打开走
execFile而非 shell:路径里的空格 / 引号 / 分号都不会变成命令(见 local-open.ts); - 回调加固(本机 docserve 与网关中继同一套四道校验):URL 签名 + DS 的 JWT +
JWT payload 与 body 逐字绑定(只验签名的话,截获一张合法 token 再改
url就能让服务去拉任意地址)+ 拉取 host 白名单(拒绝回环与链路本地/云元数据地址); - Agent 桥的准入:只接受配置里那个 Document Server 源发来的插件消息
(
event.origin校验),并把插件窗口event.source与该会话绑定一次,之后只认它; 命令只从宿主页面定向发出(event.source不可伪造)。插件侧只认OFFICE_BRIDGE_OPS的白名单, 没有任意脚本通道;命令参数经Asc.scope传递,不拼代码。
独立安装降级
- 凭据:没有
hiwork-core/ 网关未配置下发时,用本地设置的地址与密钥(界面会 标注「本地配置」);设置分区settings.section#office也相应保留; - 入口:没有
hiwork-core时(或它尚未加载),本插件注册设置页分区settings.section#office;hiwork-core后到时立刻撤下该分区并切成中央 feature; - 本地预览:与 hiwork-core / 网关完全无关,任何环境下都可用(无头环境除外—— 按钮会禁用)。
命名词汇(用户可见文案)
用户看到的品牌名统一是 HiWork Office;OnlyOffice / Document Server 只出现在
技术语境里(代码标识符、注释、文档、以及管理员要填的配置项)。
| 语境 | 用词 | 例 |
| --- | --- | --- |
| 界面标题 / 按钮 / 引导 | HiWork Office | settings.title = 「HiWork Office 设置」 |
| 界面里的功能描述 | 在线编辑器 / 本机默认应用 | file.preview.title = 「在线预览:用 HiWork Office 在线编辑器打开」 |
| 配置项(管理员要填的事实) | Document Server / JWT 密钥 | settings.serverUrl = 「Document Server 地址」 |
| 代码标识符与注释 | OnlyOffice(准确的技术名) | OnlyOfficeEditorFrame、loadOnlyOfficeApi |
同一个功能在 hiwork-core(hiworkOfficeCredentials 的消息文案)与 ai-gateway
(GET /api/me/office-config 的 503 文案)各有一份用户可见文案,改这条链上的文案时
三处要同步。
client bundle 纯度
src/client/** 对 @deepseek-ai/* 只允许 import type(打包时被擦除),运行时只
import react / react/jsx-runtime。tests/packaging.spec.ts 会在构建后扫描
lib/client.js 的 require 说明符,越界即失败。
网络要求(部署侧事实,2026-09-16 验证)
- Document Server 必须能回连本机(拉文档 + 回调):本机需有私网 IPv4,或显式配置
advertiseHost; - 浏览器(桌面端 Web UI,
http://127.0.0.1)必须能访问 Document Server (https://office-suite.hivery.cn,HTTPS 域名经反代提供;http 页面嵌 https iframe 不构成混合内容,且未来桌面 UI 升级 HTTPS 后也不受影响); - 部署侧
JWT_ENABLED=true、JWT_IN_BODY=true、ALLOW_PRIVATE_IP_ADDRESS=true; - Agent 桥不引入任何新的入向要求:插件与宿主页面只在浏览器进程内 postMessage, 用户电脑不需要被 Document Server 之外的任何东西访问到(云端中继 + 桥的组合也可用)。
命令
| 命令 | 作用 |
| --- | --- |
| pnpm typecheck | 严格类型检查 |
| pnpm build | 构建 lib/index.js(Host)与 lib/client.js(Web) |
| pnpm test | Vitest(DOM 用例首行 // @vitest-environment jsdom) |
| pnpm verify | typecheck → build → test,提交前跑这个 |
| pnpm pack | 产出 hiwork-office-<version>.tgz |
桌面端集成
本地迭代按工作区约定走 Web profile file: 安装(pnpm build && pnpm pack 后在
profile 目录 pnpm install),不必发版。要让 CI 打包 / 分发给同事时:
npm publish → 更新 hiwork-desktop/src/bundled-plugins.ts 的 BUNDLED_PLUGINS
锁定版本 → 重新打包桌面端。
让改动在已装的 HiWork 里生效(2026-09-16 踩过的坑)
重启 HiWork 不会自动升级本插件:桌面端的补种只升级它自己目录
(src/bundled-plugins.ts)里的插件,office 不在其中——它是 profile 里手工加的
file: 依赖,会一直停在那个 tarball 的版本上(曾出现"改了源码、重启没效果",
因为 profile 还指着 hiwork-office-0.1.0.tgz)。
更新已装 HiWork 的正确姿势(两个 profile:~/.hiwork/profiles/web 是桌面端的,
~/.dsh/profiles/web 是 dsh web 的):
# 必须用桌面端自带的 pnpm 11(系统 pnpm 10 会因 store 版本不同报 UNEXPECTED_STORE)
NODE="/Applications/HiWork.app/Contents/Resources/node/node"
PNPM="/Applications/HiWork.app/Contents/Resources/node/pnpm-package/bin/pnpm.cjs"
pnpm build && pnpm pack # 在本仓库产出 hiwork-office-<version>.tgz
"$NODE" "$PNPM" add file:$PWD/hiwork-office-0.3.0.tgz \
--dir=/Users/$USER/.hiwork/profiles/web \
--config.node-linker=hoisted --config.auto-install-peers=false \
--config.minimumReleaseAge=0 --save-exact --registry=https://registry.npmjs.org/装完退出并重启 HiWork 即可(插件在 Host 启动时加载,不能热替换)。要回到 registry
版本:把 profile 的 package.json 改回 "hiwork-office": "<版本>" 后用同一条命令
(或直接 add hiwork-office@<版本>)。
分支流程
按工作区约定(2026-09-09 起):先在 dev 分支开发(remote gitlab),验证通过后
合并到 master;master 只放可发布状态。
