- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次) - .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…), 目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪 - .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/) - .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*) - .gitignore 补:备份件(*.bak-*) - 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
7.7 KiB
IM 群组 UI 插件投放 · 线上取证操作单
工作区:
E:\ProgramData\AIProject\ai1net-dsh-server|线:IM 线(第 14 棒)|日期:2026-09-23 20:2x 插件:@dsh-local/im-conversation-tabsv0.1.0(E 单主交付,落点 = 实例内 dsh 会话页 tab) 投放物指纹:tgz sha256c9b1184aca8eadd6c84316d2e79427546a65f59f0614a2ec9704c895a311bfb1(21,494 B) |lib/client.jsmd521fbc52fdedc9915238dd1c3f5c5e827(= 本机源 / 包内 / 47 共享层三处逐字一致)
一、服务端侧已完成(第 14 棒,⛔ 无需再做)
| 步骤 | 结果 | 证据 |
|---|---|---|
打包(tgz,package/ 布局) |
✅ | 包内 4 文件;三件齐(exports['./client'] / dsh.client.platform='web' / dsh.bundle.patch)断言 rc=0 |
| admin 导入候选池 | ✅ http=200 | replaced:false · trustedOverride:false · compat.level=unknown(非 incompatible)· blocked 无 P0 ⇒ 未用信任放行;审计 audit_log id 360 upload_business_plugin |
| 池内可见 | ✅ 4 → 5 条 | GET /api/plugins/business 命中 @dsh-local/im-conversation-tabs v0.1.0 |
发布到共享层(link: 装配前提) |
✅ action:"created" |
目录 /var/lib/dshs/bundled-plugins/_dsh-local_im-conversation-tabs(root:root,全 world-readable);fileRef = link:/var/lib/dshs/bundled-plugins/_dsh-local_im-conversation-tabs;审计 id 361 share_business_plugin |
| 回滚素材 | ✅ | /opt/dsh/backups/plugins/_dsh-local_im-conversation-tabs/0.1.0.tgz |
| 临时 admin 会话 | ✅ 已删 | deleted_sessions=1;临时文件(payload / 响应 / 暂存 tgz)零残留 |
⚠️ 为何必须做「发布到共享层」:装配协议 = link:(2026-09-23 拍板)⇒ profile 里写的是软链依赖,目标即共享层目录。只进候选池不发布,用户点启用会撞 plugin_bundle_missing(friendlyError 把它翻成「该插件尚未发布到共享包库,请先联系管理员完成发布」)⇒ 半成品投放。
二、待用户动作(3 步,约 2 分钟)
步 1 · 在实例内启用
实例 → 设置 → 功能管理 → 找到 @dsh-local/im-conversation-tabs → 打开开关。
- 该插件未声明数据面(
dataPlane.state = none)⇒ 不会被「未建库不许开启」门禁拦。 - 点开后平台自动执行:写 profile(
link:引用)→ pnpm 建链 → 重启实例 + 探活(无需另做重启)。
步 2 · 确认实例真的带上了包(只读,一条命令)
ssh bt-server 'ls -la /var/lib/dshs/users/<你的 uid>/home/profiles/web/node_modules/@dsh-local/ | grep im-conversation-tabs'
期望:一行软链,形如 im-conversation-tabs -> …/bundled-plugins/_dsh-local_im-conversation-tabs;退出码 0。
(uid 查法:ssh bt-server 'ls /var/lib/dshs/users/',目录名 = 你的 userId。)
步 3 · 线上取证(浏览器,见第三节三条)
三、线上取证判据(生效链路第 ④ 关)
🔴 口径更正(本棒实测):IM 链路在服务端一条日志都不写 ——
src/im/ws.ts/src/web/routes/im.ts/src/supervisor/proxy.ts三处process.stderr/console.*零命中;reject()只回一帧error再destroy(),不落日志。⇒ 判据必须在客户端取,⛔journalctl | grep bad-op恒空(空 ≠ 没发生)。
| # | 判据 | 命令 / 操作 | 期望(+退出码) |
|---|---|---|---|
| ① | 会话区被接管,tab 行出现 | 浏览器 DevTools Console:document.querySelectorAll('.imtabs-bar').length |
1 |
| ② | tab 行内容(dsh 会话 + 群组 tab) | document.querySelectorAll('.imtabs-tab').length |
>= 1(首屏 = dsh 会话 tab;开群组 tab 后递增) |
| ③ | 🔴 协议对齐(原 P0 不复现) | DevTools → Network → WS → /api/im/ws → Messages:看帧流水 |
出站只有 {"op":"subscribe"…} / {"op":"ping"} / {"op":"resume"…};入站首帧 {"op":"hello"…};不出现 {"op":"error","error":"bad-op"…} |
| ④ | 连接态健康 | document.querySelector('.imtabs-conn').className |
不含 imtabs-connBad(含 connWarn 视为未通过) |
| ⑤ | 槽位命中(失效时可见) | document.querySelector('.imtabs-alert') |
null(非 null ⇒ 官方槽名又变了,见 .imtabs-alert 提示文案) |
| ⑥ | 房间消息真的渲染 | 在房间里发一条消息 → 群组 tab 内消息流 | 消息出现在列表中(游标补齐走 REST pull + WS resume,两侧按 m.id 去重) |
判定口径:①②③④⑤⑥ 全过 ⇒ E 单「线上可见面」通过,E 单可判「已交付」;任一条不过 ⇒ 本项记「未通过」,把 ③ 的帧流水原文回传(那是定位 P0 类的唯一凭据)。
四、前置条件与两个已知缺口
- 🔴 实例在哪台机决定成败:
- 实例在 47(Manager) ⇒ 共享层已就绪,直接可用 ✅(47 现有实例 =
admin,uid114801)。 ⚠️ 47 上该实例的 bwrap 当前没有--ro-bind-try /var/lib/dshs/bundled-plugins(启动于 09-21 00:55,早于该绑定上线)⇒ 必须让它走一次重启(步 1 的启用动作自带重启,即已满足)。 - 实例在 106(worker) ⇒ 共享层缺该包(106 只有
_dsh-local_storyforge/dsh-plugin-mcn-suite)⇒ 启用会失败。已知缺口:跨节点内容分发无平台侧通路(投放线 §5-2 记为「需单独立项」)。临时办法(手工同步,沿用第 8 棒做法):(第二条会整体覆盖 106 的 manifest ⇒ 执行前先比对两边内容,只差新增那一条才可覆盖。)ssh bt-server 'tar -C /var/lib/dshs/bundled-plugins -czf - _dsh-local_im-conversation-tabs' \ | ssh test106 'tar -C /var/lib/dshs/bundled-plugins -xzf - && chown -R root:root /var/lib/dshs/bundled-plugins/_dsh-local_im-conversation-tabs' ssh bt-server 'cat /var/lib/dshs/bundled-plugins/.manifest.json' | ssh test106 'cat > /var/lib/dshs/bundled-plugins/.manifest.json'
- 实例在 47(Manager) ⇒ 共享层已就绪,直接可用 ✅(47 现有实例 =
- ⚠️ admin 实例 profile 里有一条陈旧软链(
node_modules/@dsh-local/storyforge,09-21 建立,但dependencies/bundles里都已无该项)—— 第 8 棒探针残留。本轮只报告未动(R7)。与本次取证无关。
五、回滚(任一步出问题)
| 目的 | 动作 |
|---|---|
| 只关插件(保留投放) | 实例 → 设置 → 功能管理 → 关掉开关(平台自动 pnpm remove + 重启探活) |
| 撤出用户面(停止发布) | POST /api/plugins/business/share 的逆操作:DELETE /api/plugins/business/:id/share(⛔ 若仍有用户启用会被 409 挡下 —— 这是设计) |
| 彻底下架 | DELETE /api/plugins/business/%40dsh-local%2Fim-conversation-tabs(清池行 + tgz) |
| 换回旧包 | 回滚素材 /opt/dsh/backups/plugins/_dsh-local_im-conversation-tabs/0.1.0.tgz |
六、本单未做 / 未验(⛔ 不假装做了)
- ⛔ 未替用户点启用 —— 「启用」是用户动作(投放语义 = 用户自助开通),本单只把可自动部分做完。
- ⛔ 未做网关部署(选型单 §十五 的 gateway 侧部署)|⛔ 未接在线态取数(
stats().presenceIngest='not-wired')—— 两项与本次投放解耦,排在再后。 - ⛔ 未同步 106 共享层 —— 见 §四-1,属「跨节点内容分发」立项面;本单给出命令但未执行(避免无谓的多机生产改动)。
- ⛔ 未 commit / push / scp 源码 | ⛔ 未改
poc/im-conversation-tabs/**与src/im/**任何一行 | ⛔ 未引新依赖 | ⛔ 未铺 profile。