- 变更规模:新增 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/ 知识文件,按口径入库)
124 lines
15 KiB
Markdown
124 lines
15 KiB
Markdown
# 接续包 · IM 线 · 2026-09-26 00:32
|
||
|
||
## 接续点 · IM 线(插件接入面收尾 + 文档 + 对接答复) · 2026-09-26 00:32
|
||
|
||
- **来源会话**: 本会话(阿里云 dsh 平台开发会话) | **结束原因**: 上下文 32.8 万强制收口
|
||
- **原目标**(用户本轮原话,不翻译、不缩写):
|
||
1. 「**创建接续会话把IM的改造优化处理完成**」
|
||
2. 「**然后更新IM接入文档**」
|
||
3. 「**并回复插件的对接文档**」
|
||
- **基线**: HEAD=`e6207aa`(`D:/github/dsh_shenxian`,**未 commit**) | 远端 master = 未核(本会话未 push) | 域锁 = **无**(已释放)
|
||
- **产物**(新会话必须逐一确认存在):
|
||
- 代码面(`D:/github/dsh_shenxian`):`src/im/sdk/types.ts`(+206)· `src/im/sdk/host.ts`(+362)· **`src/im/sdk/binding.ts`(新)** · **`src/im/sdk/registry.ts`(新)** · `src/im/sdk/index.ts` · `src/im/hub.ts`(+36)· `src/im/ws.ts`(+84/−1)· `src/web/routes/im.ts`(+368)· **`sdk/im-plugin-host/index.mjs`(新)** + `package.json`
|
||
- 测试(新):`test/im-plugin-bridge.test.mjs`(14 例)· `test/im-backpressure.test.mjs`(8 例)
|
||
- 文档库(`D:/github/dsh_shenxian/dsh-server-docs`):`01-规范/09-IM插件SDK与扩展点契约.md`(§2 加第 8 项 · §3.5 接线器 · §6 修事务口径 · §8 判据四档 · §9 出向端口 · **§10 跨进程接入** · **§11 投递可靠性与容量**)· `01-规范/08-插件开发与对接规范.md`(修路由指针 + SDK 获取 + 09 指针 + §11 变更记录)
|
||
- 评估/交付件(本工作区 `交付物/`):`IM插件接入面-20260925.md` · `IM插件接入完备性审计-20260925.md` · `用户组网群聊可行性评估-20260925.md` · `端口与容量瓶颈评估-20260925.md` · `端口决策与设备中继可行性-20260926.md` · `扇出背压修复-20260926.md`
|
||
- **校验命令**: `cd /d/github/dsh_shenxian && npm run build && node --test test/im-plugin-bridge.test.mjs test/im-backpressure.test.mjs 2>&1 | tail -5`
|
||
→ **期望输出**: 两个文件合计 **22 例、`fail 0`**(bridge 14 + backpressure 8)
|
||
- **未完成**(用户要的、还没做的 —— 优先于 AI 自己加的收尾):
|
||
|
||
> ## ✅ 本棒(「IM接入面收尾-20260926」· 2026-09-26 00:38–01:0x)收口状态
|
||
>
|
||
> **已完成**
|
||
> - **1-① 端到端真跑 ✅**(真起平台 + 真 HTTP + 插件侧 SDK 走 5 端点):
|
||
> 脚本 `tmp/im-bridge-e2e-20260926.mjs`,**23 例 / fail 0**。⇒ 跑法 `cd /d/github/dsh_shenxian && node <脚本绝对路径>`。
|
||
> - **1-② 部署到 47 ✅**:推 `lib/im/**` + `lib/web/routes/im.js` + `sdk/**`(⛔ 未带别的线的未部署改动)。
|
||
> 47 侧备份 `/opt/dsh/backups/im-bridge-predeploy-20260926.tgz`;重启 `dshs` = active;
|
||
> `/api/im/plugins/bridge` 与 `/api/im/stats` 均 **401(= 路由已加载,非 404)**。
|
||
> ⚠️ **落地文件属主**:tar 会保留本地 uid ⇒ 47 上新文件曾变 `197108`,**已 chown -R root:root 修回**。
|
||
> (⛔ 下棒再推时给 tar 加 `--owner=root --group=root`,或推完立刻 chown。)
|
||
> ⚠️ 47 上另有**先前就存在**的非 root 文件(`lib/web/routes/business-plugins.js`、`admin.js`、
|
||
> `essential-plugins.js` 等)——**非本棒落地、未动**,仅登记。
|
||
> ⛔ **106 未部署**(IM 宿主在 Manager 进程,Worker 不带 IM 路由)⇒ 按需为「不需要」。
|
||
> - **3 更新 IM 接入文档 ✅(部分)**:09 新增 **§12 插件作者快速上手**(五步 + 可跑片段 +
|
||
> 能做/不能做 + 四个必知坑 + 反向通道降级三条)。⛔ **未动任何已落章节**(§2–§11 一字未改)。
|
||
> ❌ **仍缺**:可跑的最小**示例工程**(`poc/business-plugins-im/` 三例仍是旧契约)。
|
||
> - **4 插件对接答复件 ✅**:本工作区 `交付物/插件对接答复件-20260926.md`(⛔ 未改 `dsh-plugin-partment` 一字节)。
|
||
>
|
||
> **🔴 本棒抓出并修掉一个真缺陷(新增,不在原接续包内)**
|
||
> - **症状**:插件走完登记后 `frame()` / `say()` **恒 `plugin-unavailable`**。
|
||
> - **根因**:`routes/im.ts` 的 `bridgeAuth` 把**请求头里的包名**(`@dsh-local/im-plugin-demo`)
|
||
> 直接当内部 id 去查注册表,而注册表按 `pluginIdOf(包名)`(`im_plugin_demo`)存 ⇒ **恒查不到**。
|
||
> ⇒ **出向面在生产上不可达**(第 8 项等于白做)。
|
||
> - **为什么单测没抓到**:`test/im-plugin-bridge.test.mjs` **刻意不碰真实网络**(mock fetch +
|
||
> 直接把归一化 id 递给注册表)⇒ **从不经过这一层翻译**。只有真 HTTP 才暴露。
|
||
> - **修法**:`bridgeAuth` 现同时交 `packageName`(供"头与清单自洽"判)与 `pluginId`
|
||
> (供注册表查);两处 `outboundPortFor` 改用 `auth.pluginId`。**改动仅限 `routes/im.ts`**
|
||
> (内核四档不变:`store.ts` 零改动 / `hub.ts` 纯插入 / `ws.ts` 只动投递点 / `routes/im.ts` 纯插入 ——
|
||
> 本次属"修 bug"那类,故允许动既有逻辑,须有回归证据)。
|
||
> - ⚠️ **回归证据未入库**:现为 `tmp/` 脚本(接续包原口径即"临时脚本落 tmp/")。
|
||
> 若要**永久回归**⇒ 下棒需把它落成 `test/im-plugin-bridge-e2e.test.mjs` + 在 `package.json`
|
||
> 的 `test`/`verify` 各登记 1 处(A=B 各 1 行,⛔ 用 Edit 精确替换)。
|
||
>
|
||
> **⛔ 仍未完成(下棒依据)**
|
||
> - **1-③ 生产标定背压阈值 ❌ 未做(缺真实读数)**:`/api/im/stats` 现为 **401(admin 专属)**,
|
||
> 无 admin 会话读不到 `peakPendingBytes`;且当前无真实流量 ⇒ 读数恒 0,**改了也是拍脑袋**。
|
||
> ⇒ **阈值保持 4 MiB 不变**(保守起点),**改参数入口已就绪**(`IM_SEND_BUFFER_LIMIT_BYTES`,可注入)。
|
||
> 标定法(拿到 admin 会话或真实流量后照做):连读 `GET /api/im/stats` 的 `peakPendingBytes` 一段时间,
|
||
> 取其分位值作为阈值下限,改 drop-in 环境变量 → `systemctl restart dshs`(⛔ **不改代码**)。
|
||
> - **2 反向通道** —— 🔴 **2026-09-26 用户已拍板:必须补、且要做完整**
|
||
> (原话「**都需要实现完整,才能让插件接入,否则两边都要返工**」)。
|
||
> 本棒据此**改了 09 §10.4 / §10.5 / §12.5 的口径**(从"本期不支持"→"已定必须补")+
|
||
> 同步 `交付物/IM插件跨进程桥-20260925.md §5/§6` 与 `交付物/插件对接答复件-20260926.md`,
|
||
> 并产出**施工规格** `接续包_IM反向通道实现_20260926.md`。
|
||
> ⚠️ **实现交执行棒**(本棒受会话预算限制,⛔ 未动任何代码);⛔ 原文"不许拍成已定"的禁令**至此解除**
|
||
> —— 因为**是用户本人拍的**,不是新会话替原会话拍的。
|
||
> - **§1 对接单未消的 4 条**(纯文档):**#2 表声明入口(P1,需先定口径)** · #4 移动端 ·
|
||
> #5 错误码/自测清单/变更记录 · #8 内部编号 107/108。
|
||
> ⚠️ **接续包原记「P1×2 已消」不准**:实测 **#1 已消、#2 未消**(09 §4 仍有 `tables: [{` 且全篇无 `dsh.data.schema`)。
|
||
|
||
1. 🔴 **IM 改造的收尾三件**(= 用户「处理完成」的落地口径,按序):
|
||
① **端到端真跑验收** —— 桥的 5 个端点(`/api/im/plugins/{register,unregister,out/frame,out/message,bridge}`)**只过了单测,从未真跑**;需在本机起平台+用 `sdk/im-plugin-host` 走一遍真 HTTP。
|
||
② **部署到 47(+按需 106)** —— 本机代码面完成,**生产未部署**(R8:开发环境服务器,动手前一句话说明即可)。
|
||
③ **生产标定背压阈值** —— 现为保守起点 4 MiB;观测面 `GET /api/im/stats` 的 `peakPendingBytes` 拿到真实读数后**改参数**(⛔ 不改代码)。
|
||
2. 🔴 **「反向通道」是否补 —— ⛔ 属待用户拍板,**不许新会话替原会话拍成「已定」**:
|
||
- 现状:**「插件单向发起」已通**(登记 / 读 / 出向);**「平台回头叫插件」未通**(`speakRules.check` 发言规则、`events.onEvent` 事件回调)。
|
||
- 原会话判断(**可推翻**):「**不必急着补**」—— 已有降级三条(规则参数写房间 `config` / 出向主动纠偏 / `host.subscribe()` 轮询替代事件回调,后者已实测够用)。
|
||
- 若要补 ⇒ 需要**平台 → 实例的反向调用面**(含鉴权、超时、实例可寻址性),是新工作量。
|
||
3. **更新 IM 接入文档**(用户第 2 件事):`09` 已更新到 §11,但**缺一份「插件作者快速上手」** —— 现在用法散在 §10.3,且**没有可跑的最小示例工程**(`poc/business-plugins-im/` 三个示例是**旧契约**,未含第 8 项出向与跨进程桥)。
|
||
4. **回复插件对接文档**(用户第 3 件事):即 `E:/ProgramData/AIProject/dsh-plugin-partment/对接单_平台侧-IM板块待优化与Macro改造可行性_20260925.md`。⚠️ **那是别的工作区 ⇒ ⛔ 只能在本工作区产出「平台侧答复件」,由用户转交,⛔ 不得直接改那边一个字节**。答复要覆盖:§1 九条(P1×2 + P2×3 **已消**,P2×2 + P3×2 待定)+ §2 Macro 改造可行性(第 8 项出向端口**已具备**,§2-3-1 高频广播**已解决**:`frame` 档不落库)+ §4 两个待拍板项的**平台侧回答**。
|
||
- **下一步**(第 1 个动作 = 具体命令):
|
||
1. **先跑校验命令**(见上)—— 不符即停、只报告。
|
||
2. 读 `交付物/IM插件跨进程桥-20260925.md` 与 `交付物/扇出背压修复-20260926.md`(本会话两份最新落地件,含未完成项与设计取舍)。
|
||
3. **端到端真跑**(未完成 1-①):本机起平台(`DSH_IM_WS` 默认开)→ 写一个临时脚本用 `sdk/im-plugin-host` 的 `createImPluginHost()` 走 `register → frame → say` → 断言 `ok:true`(**临时脚本一律落 `tmp/`,⛔ 不入库**)。
|
||
4. 部署(未完成 1-②)→ 标定(1-③)→ 文档(3)→ 答复件(4)。
|
||
5. ✅ **未完成 2(反向通道)—— 用户已表态**(2026-09-26:必须补、做完整)⇒
|
||
**下棒依据改为 `接续包_IM反向通道实现_20260926.md`**(⛔ 不再是"先不碰")。
|
||
- **关键决定**(已定项 + 为什么 —— 防新会话推翻重来):
|
||
① 🔴 **插件跑在「用户实例内」、IM 宿主在「平台进程」⇒ 跨进程** —— 依据:用户自配模型是起实例时写进实例的(`credential_vault` → spawn 写 `settings.yaml`),平台侧拿不到 ⇒ 插件必须在实例内。
|
||
② **桥 = 插件「拨出」而非平台「打进」** —— 符合既有"拨出式"纪律(`09 §2-6`:⛔ 插件不开监听端口),且断连可退化为普通聊天。
|
||
③ **登记只收「声明面」** —— `check`/`onEvent`/`render`/`onAction` 是函数、跨不了进程;函数侧回调留在实例内。
|
||
④ **出向发言多一道闸** —— bot 必须是该房成员(同进程时必然成立,跨进程必须自己查);未注入成员判定 ⇒ **fail-closed 拒**。
|
||
⑤ 🔴 **背压:慢客户端不虚减 `delivered`** —— `write()` 返回 false = 水位满(**数据已排队、会送达**)⇒ 若记成丢帧会污染观测。**若用户要"慢客户端单独计 skipped"⇒ 是语义变更、需重新拍板**。
|
||
⑥ **判据分两类**(已写进 `09 §8`):**新增能力 ⇒ 纯插入**;**修 bug ⇒ 可改既有逻辑**,但须「回归用例 + 新行为可观测 + 阈值可注入」。内核四档:`store.ts` 零改动 / `hub.ts` 纯插入 / `ws.ts` 允许改但**被删行只限投递点** / `routes/im.ts` 纯插入。
|
||
⑦ **端口 D8 = 用户拍板「先不开,后续有需要再开」**(09-26)⇒ ⛔ 不当待办、⛔ 不催;群聊全走 443 中继,功能完整。
|
||
⑧ **设备当中继**:路径已存在(桌面端节点接入已实测),但**中继方需"别人能连到它"**=用户侧 NAT 门槛;**推荐替代 = 设备主动拉**(⛔ 不需任何端口)。⛔ 未排期。
|
||
- **回滚点**:`D:/github/dsh_shenxian` HEAD=`e6207aa`(**本会话全部改动未 commit** ⇒ `git checkout -- <file>` 即可回到干净基线);`dsh-server-docs` 同理;部署前须另做 47 侧备份(`/opt/dsh/backups/`)。
|
||
- **⛔ 不要重做**:
|
||
- **扩展点第 8 项(出向端口)+ 面板动作 + 分片工具**(types/host 已落,22 例测试覆盖)
|
||
- **跨进程桥平台侧**(`registry.ts` + 5 端点 + `bridgeAuth`)
|
||
- **插件侧 SDK**(`sdk/im-plugin-host/index.mjs`)
|
||
- **背压修复**(`ws.ts` 1 行改写 + `hub.ts` 计数 + 8 例)
|
||
- **两处规范文档**(09 已到 §11;08 已修指针)—— 只**补**「快速上手」,⛔ 不重写已落章节
|
||
|
||
---
|
||
|
||
## §A 本轮(原会话)做了什么 —— 一句话
|
||
|
||
把「IM 基础」从**插件接不进来**改造成**双向可接入**:① 补出向出口(扩展点 8)② 补跨进程桥(登记 / 出向,插件侧 SDK 齐备)③ 修扇出背压隐患;并把两处规范同步到 §11。
|
||
|
||
## §B 判据读数(原会话实测,供新会话对照基线)
|
||
|
||
- `npm run build` **rc=0**
|
||
- `npm test` = **626 / 616 过 / 5 败 / 5 跳过** —— ⚠️ **5 败是既有环境问题**(投放线 `test/shared-layer-sync.test.mjs` 的 `spawnSync tar EBUSY`),**与本线无关**,⛔ 不要去修它(属投放线 lane)
|
||
- 本线专项:`test/im-plugin-bridge.test.mjs` **14/14** · `test/im-backpressure.test.mjs` **8/8** · `test/im-sdk.test.mjs` **78/75 过 / 3 跳过**(跳过 = `spawnSync git EBUSY`,已改具名跳过)
|
||
- `npm run check:layering` **rc=0 ✅ 无新增违规**
|
||
- 内核改动形态:`src/im/store.ts` **零改动**;`ws.ts` 被删改的既有行**只有 1 行**(`socket.write(textFrame(text))`,正是背压修复点)
|
||
|
||
## §C 工具纪律(本线踩过的)
|
||
|
||
- ⚠️ **测试跑的是 `lib/` 编译产物 ⇒ 改完必 `npm run build`**(原会话踩过:`hub.ts` 新、`ws.ts` 旧混跑 ⇒ 症状像"注入无效",浪费两轮)。定位法 = 打印**两个独立观测点**(注入值 ✅ 但计数为 0 ⇒ 走的是旧代码)。
|
||
- ⚠️ 本机 `spawnSync git|tar EBUSY` 会让测试**假失败** ⇒ 取证类用例改 `t.skip` 具名跳过。
|
||
- 🔴 **改 `package.json` 一律用 Edit 精确替换**,⛔ 不用「读-改-写整个 JSON」(会重排格式)。原会话实测侥幸未炸,但那是运气。
|
||
- 🔴 (「接续包 md5」口径门禁)开工前重算 md5 与 prompt 里的一致才开工;不一致 ⇒ 停手报告。
|