1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
+ ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
6.4 KiB
6.4 KiB
75 · 插件评估新增「托管友好性」与「资源成本」两个维度(2026-09-12)
- 日期:2026-09-12
- 状态:📋 标准已立(本档只立标准,未改预检脚本)
- 触发:
dsh-univer-office静态预检判ok却在平台上完全不可用,且加载成本 +65.1 MiB → 用户提出「把这个作为插件评估和改造的一个点」 - 范围:评估维度(候选池导入预检)+ 改造规范(自研 / 改造插件)
- 关系:接续档案 71(插件兼容性预检:依赖范围 + 导出符号),本档是新增维度,不改变其机制与分级语义
一、为什么需要(案例暴露的两个盲区)
dsh-univer-office 在档案 71 的静态兼容性预检里判 ok(依赖与导出符号全满足)——但它在平台上完全不可用:
| 预检现状 | 案例暴露的盲区 |
|---|---|
| 查 semver 依赖范围 | ✅ 通过(依赖没问题) |
| 查运行时导出符号 | ✅ 通过(符号都在) |
| ❌ 不查「它假设的部署形态」 | 把 http://127.0.0.1:${port} 交给浏览器 → 托管平台必然失效,且平台侧无法补救 |
| ❌ 不查资源成本 | 加载 rss +65.1 MiB(实测)= 实例配额的 1/6 |
⇒ 兼容 ≠ 可用。 需要补两个维度。
二、新维度 A:托管友好性(Hostability)
判据(一句话)
插件的一切对外交互,是否都能走「平台已有的那一条入口」? 即:浏览器侧只用相对路径;实例侧不依赖本机 loopback 上的网络服务。
检查项
| ID | 检查 | 检测方法(复用档案 71 的 walkJs 框架) |
等级 |
|---|---|---|---|
| H1 | bundle 里出现硬编码 loopback 服务地址 | 文本扫描 127.0.0.1:<port> / localhost:<port> |
🟡 出现即警告 |
| H2 | client bundle 里出现绝对 URL(http:// 字面量) |
只扫 client 侧产物;命中即「会交给浏览器」 | 🔴 阻断 |
| H3 | 自建网络监听且非 unix socket | 扫描 listen( / createServer 参数形态 |
🟡 需人工确认是否 loopback-only |
| H4 | 交付浏览器的 URL 由 Host 生成,还是客户端自行拼接 | 人工研判(架构文档 + client 代码) | 🔴 若客户端自拼绝对地址 |
为什么 H2 是阻断级:绝对地址一旦交给浏览器,在托管平台上必失效——跨机器(127.0.0.1 指向用户本机)+ HTTPS 混合内容会被浏览器硬拦,且平台侧无补救手段(不能靠配置、不能靠反代)。这正是本案例的死因。
反面样板(本案例实测)
viewerUrl = `${gateway}/?file=...` // ← 交给浏览器的绝对 URL
gateway = `http://127.0.0.1:${port}` // ← 硬编码 loopback,且被浏览器 iframe 使用
两条独立阻碍,缺一都打不开:
- Host → Gateway:实例内进程 uid 100002 →
127.0.0.0/8被 nftreject with tcp reset - Browser → Gateway:绝对 loopback URL,浏览器在用户电脑上 → 即使解封第 1 条也打不开
正面样板(同一插件内)
- Client(浏览器模块):只走
/univer-api/*同域相对路径 → ✅ 平台可反代 - 平台自研 ×3:加载成本 0.0 MiB、无自建监听 → ✅
三、新维度 B:资源成本(Resource Cost)
| 项 | 判据 | 方法 |
|---|---|---|
| 加载内存 | 插件 import 后的 rss 增量 |
隔离 cgroup 内逐个 import 实测(node --expose-gc,MemoryMax 兜底) |
| 标注阈值 | > 30 MiB → 标注;> 60 MiB → 强提示 | 同上 |
实测基线(guest 实例,2026-09-12)
| 插件 | rss 增量 |
|---|---|
dsh-univer-office |
+65.1 MiB |
libsql(间接依赖) |
+7.8 MiB |
| 平台自研 ×3(portal-entry / workspace-scoped-picker / business-plugins) | 0.0 MiB |
puppeteer-core(仅 import 主入口、未启动浏览器) |
0.0 MiB |
⇒ 成本由「装了什么」决定,不是「装了几个」 —— 一个重型插件 ≈ 无穷多个轻量插件。 ⇒ 平台自研插件的 0.0 MiB 是正面基线,应作为自研插件的验收参考。
四、改造规范(自研 / 改造插件必须满足)
| # | 规范 | 理由 |
|---|---|---|
| R-a | 对外入口唯一:浏览器可达面全部挂在 Host 的 webServer(同域相对路径) | 复用平台反代,零新增暴露面 |
| R-b | 内部通信走 IPC:unix domain socket 或 stdio,不用 TCP loopback | 不经 IP 层 ⇒ nft 管不到;天然无端口冲突 |
| R-c | 不新增监听端口 | 多租户同主机下,端口 = 跨租户风险 + 端口漂移 |
| R-d | 重型依赖懒加载:原生绑定、引擎等改 await import() |
实测有效:puppeteer-core 仅 import 主入口 = 0.0 MiB |
| R-e | 交付浏览器的地址一律由 Host 生成且为相对路径 | 绝对地址在托管平台必失效 |
五、落地方式(未实施,待排期)
| 步 | 动作 | 落点 |
|---|---|---|
| 1 | 兼容性预检加 H1/H2 静态扫描(复用 walkJs) |
dsh-server-docs/scripts/plugin-compat-check.mjs |
| 2 | H2 命中 → 判 blocked,理由「向浏览器交付绝对地址,托管平台不可用」 |
同上(沿用档案 71 的分级语义) |
| 3 | 资源成本:预检阶段先做静态体积提示(解包后 artifacts/ 体积),精确值留隔离实测 |
同上 |
| 4 | 自研插件按 §四 规范执行(T03 的 MCN suite 起适用) | 交接单/T03 |
⚠️ 本档只立标准。改预检脚本属生产代码变更,须另立交接单执行(规划与执行分离)。
六、与既有档案的关系
| 档案 | 关系 |
|---|---|
| 71(兼容性预检:导入/上传即判定) | 本档是其新增维度,机制与分级语义沿用,不替换 |
| 66(业务插件 P0 误报 → admin 显式信任) | 本档 🟡 级沿用「放行 + 标注 + admin 可见」口径 |
| 70(anysearch 不兼容致崩溃循环) | 同属「预检盲区导致线上事故」,本档是系统性补盲 |
| 74(实例内存治理:V8 堆限 256→160) | §三 资源成本维度与 74 的配额治理同源 |
七、决策记录
- 2026-09-12 · 用户提出「把这个作为插件评估和改造的一个点」→ 本档立标准
- 待定:是否立即实现 §五 步骤 1–3(需另立交接单)