Files
dsh_ai1net_server/交付物/IM群组-ui投放与线上取证操作单-20260923.md
T
admin ce8e6ceed9 chore(工作区): 纳入版本控制基线(回收 411 MB 过程产物)
回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复:
- 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录)
- tmp/(32.4 M,按接续棒命名的过程临时区)
- .workbuddy/tmp/(39.5 M)
- 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物)
- tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留

入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与
接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、
.workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。

排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、
打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
2026-09-24 07:51:03 +08:00

97 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IM 群组 UI 插件投放 · 线上取证操作单
> **工作区**:`E:\ProgramData\AI技能\aliyun-dsh-server`|**线**:IM 线(第 14 棒)|**日期**:2026-09-23 20:2x
> **插件**:`@dsh-local/im-conversation-tabs` v`0.1.0`(E 单主交付,落点 = 实例内 dsh 会话页 tab)
> **投放物指纹**:tgz sha256 `c9b1184aca8eadd6c84316d2e79427546a65f59f0614a2ec9704c895a311bfb1`(21,494 B)
> |`lib/client.js` md5 `21fbc52fdedc9915238dd1c3f5c5e827`(= 本机源 / 包内 / 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 · 确认实例真的带上了包(只读,一条命令)
```bash
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 类的唯一凭据)。
---
## 四、前置条件与两个已知缺口
1. 🔴 **实例在哪台机决定成败**:
- 实例在 **47(Manager)** ⇒ 共享层已就绪,**直接可用** ✅(47 现有实例 = `admin`,uid `114801`)。
⚠️ 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 棒做法):
```bash
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'
```
(第二条会整体覆盖 106 的 manifest ⇒ 执行前先比对两边内容,只差新增那一条才可覆盖。)
2. ⚠️ **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` |
---
## 六、本单未做 / 未验(⛔ 不假装做了)
1. ⛔ **未替用户点启用** —— 「启用」是用户动作(投放语义 = 用户自助开通),本单只把**可自动部分**做完。
2. ⛔ **未做网关部署**(选型单 §十五 的 gateway 侧部署)|⛔ **未接在线态取数**(`stats().presenceIngest='not-wired'`)—— 两项与本次投放解耦,排在再后。
3. ⛔ **未同步 106 共享层** —— 见 §四-1,属「跨节点内容分发」立项面;本单给出命令但**未执行**(避免无谓的多机生产改动)。
4. ⛔ 未 commit / push / scp 源码 | ⛔ 未改 `poc/im-conversation-tabs/**` 与 `src/im/**` 任何一行 | ⛔ 未引新依赖 | ⛔ 未铺 profile。