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 一律写「远程服务器」。
41 KiB
T03 · 把 plugin_package 7 个自研插件整合为 1 个功能插件并投放
- 日期:2026-09-12
- 状态:✅ 已完成并归档(2026-09-13 18:0x) —— 投放链路全通(上传 → admin 装 → guest 启用 → 实例重启探活成功);主体验收通过。收尾记录见 §十
⛔ 2026-09-13 09:4x 现场取证:这一步已被试过,但被平台自动回滚了。 guest 在 07:42:52 通过「功能管理」提交了启用(
POST /api/plugins/mine/apply,任务c8ac2686539aeb7c), 随后实例 07:43–07:45 连崩 5 次(exitCode=134,栈 = V8JsonParser/HeapAllocator⇒ V8 堆打满 abort), 07:46 触发熔断,平台自动回滚(package.json.bak-rollback-20260913T0748)⇒ 现在 guest bundles 里没有 mcn-suite。 根因指向--max-old-space-size=160太紧(档案 74 的下调)+ guest cgroup 峰值已 382/384 MiB ⇒ 先把内存预算调上去(需重启窗口),再重试启用。详见04-调整方案/74文末「现场反证」。
原状态行:⏳ 待续做(2026-09-13 07:3x 复核更正) —— 步骤 1–9 已完成,且「上传候选池」亦已完成(现行池内 = dsh-plugin-mcn-suite @0.3.9,09-13 00:02 上传;admin 侧已装 0.3.9 且 host 面 3 个注册加载通过),剩:guest 侧在实例「功能管理」启用(POST /api/plugins/mine/apply —— 用户动作,平台不代劳)→ 重启实例 → §八验收 → 归档。2026-09-12 14:55 已释放占用锁(原 exec-session-B),接手者直接续做;产物与验收证据见 交接单/README.md §一 的 T03 行。
🔎 只读核查结论(2026-09-12 20:2x)—— 执行前的三个事实 ① 候选池里只有
dsh-univer-office(39.9 MB) ⇒ §七 防线②"下架旧 7 包"实际无需做:那 7 个包从未上架过候选池,不存在新旧并存。 ② guest 的profile/webbundles =base / dsh-web-app / business-plugins / portal-entry / workspace-scoped-picker / @liustack/modlens / dsh-univer-office⇒ 不含任何旧包 ⇒ §七 头号风险(新旧并存 →duplicate loader entry id崩实例)在本单不成立,切换顺序可简化。 ③ 时机未到:guest 活动实测 ——ws/MCN短视频创作mtime = 20:06、sessionsmtime = 20:02(距核查 13–17 分钟)⇒ 判定活跃中,按 R8「能避开活跃时段就避开」 暂缓"启用 + 重启"。上传(零影响)与启用(断 guest ~1 分钟)同窗口做更安全(避免为"上传"单独占一次 op-lock),故整体等待空闲窗口(判据:最近 30 分钟无活动)。⚠️ 同窗口须一并处理:guest 的 bundles 里仍挂着
@liustack/modlens—— 该包今天已从候选池下架(档案 70 收尾),但用户 profile 仍启用它(包文件尚在node_modules/@liustack/modlens,故当前不致命)。这是"已下架却仍启用"的不一致态 ⇒ 与 T03 共用同一次重启窗口摘除(从 bundles 移除 + 重启),避免两次中断用户。
✅ 上线进展(2026-09-12 22:0x)
| 步 | 结果 |
|---|---|
| ① 首传 0.3.0 | ❌ 被 T05 兼容性预检拒收(HTTP 409 compat_incompatible)—— 判据 @deepseek-ai/dsh-client-ui-sidebar@^0.1.0-rc.5 vs 平台 0.1.2-rc.1。根因是 semver 的 prerelease 规则:带 prerelease 的版本只能被「同 major.minor.patch 且带 prerelease」的比较器匹配,^0.1.0-rc.5 里没有 tuple (0,1,2) 的比较器 ⇒ 判不满足。不是误报,是声明写窄了(且 T05 16:35 才上线,而包 11:15 打的,单子当时没这道关) |
| ② 修正 + 重传 | ✅ HTTP 200,compat.level = ok、findings[] 空。改法:3 个 dsh-client-* peer 范围 → ^0.1.2-rc.1(跟随平台版本);版本号 0.3.0 → 0.3.1;npm pack 重打包 → md5 adcb134e… / 322 条目。内容与 0.3.0 逐项对齐:SKILL.md 18=18、lib/ 文件 19=19、web/ 1=1(条目数差异纯属 npm pack 不写目录条目) |
| ③ 候选池 | 现 2 条(09-13 07:3x 复核):dsh-plugin-mcn-suite @0.3.9(09-13 00:02;admin 已装并验通,guest 未启用)+ dsh-univer-office @0.2.15(09-12 23:19,fork 版)|历史:曾为 @0.3.1 + @0.2.14,@liustack/modlens 与 anysearch 已下架 |
| ④ 剩余 | guest 在实例「功能管理」里启用(POST /api/plugins/mine/apply —— 用户动作,平台不代劳)→ 重启 → §八 验收 → 归档。@liustack/modlensbundles 与 node_modules 均已无该包) |
⚠️ 给接手人的两条提醒
- 上传前必过兼容性预检(
POST /api/plugins/business会 409);trust只用于"已人工确认后放行",别拿它盖住声明问题。- peer 版本范围必须跟随平台 dsh 版本(当前
0.1.2-rc.1)—— 平台升版时同步改,否则 prerelease 规则会再次拒收。插件源码并不 import 这些包(只靠dsh.client.inject注入点),所以放宽范围无功能风险。
- 触发:用户「规划
D:\dshworkspace\plugin_package整合成一个插件,可打包后由 admin 整体上传到 dsh 服务器插件,用户可以启用进行使用」 - 关联:档案 16(插件三层归属 + 投放/启停流程)、27(MCN 插件平台化评估,P0 清单来源)、34(启用探活 + 快照回滚)、36/38(铺开链路)、41(技能启停 API)、58(实例内存配额)、60(分区改名「功能管理」)、19 §C6 + 41(上传安全扫描)
- 规划会话边界:本单由规划会话产出,只读普查了本机
D:\dshworkspace\plugin_package(未 ssh、未改码)
一、目标
把 D:\dshworkspace\plugin_package 下的 7 个自研插件整合成 1 个功能插件包:admin 在门户一次性上传候选池 → 用户在实例设置「功能管理」分区启用一次 → 重启实例 → 全部功能可用。
做完的判定:一个全新用户启用该整合包并重启后,侧边栏入口 + 工作台各功能页逐个可用,journald 无 duplicate loader entry id、无崩溃循环;并且业务技能随包自动就位(见 §4.1)—— $DSH_HOME/skills/ 出现 mcn-short-video 与 4 个业务子技能,用户不需要另外上传或启用技能。
二、现状(2026-09-12 只读普查结论)
| 包 | loader id | 体积 | client 行 | host 行 | 角色 |
|---|---|---|---|---|---|
dsh-plugin-mcn |
mcn-nav |
945K | 3045 | 3095 + 9 模块 | 宿主壳(唯一声明 dsh.client.inject=[runtime, sidebar]) |
dsh-plugin-douyin-accounts |
douyin-accounts |
55K | 666 | 9(空壳) | 功能页 |
dsh-plugin-douyin-account-detail |
douyin-account-detail |
67K | 877 | 9(空壳) | 功能页 |
dsh-plugin-douyin-video-detail |
douyin-video-detail |
39K | 496 | 9(空壳) | 功能页 |
dsh-plugin-mcn-schedule |
mcn-schedule |
54K | 253 | 483 | 功能页 + 定时任务 |
dsh-plugin-social-workbench |
social-workbench |
37K | 150 | 218 | 外部服务守护(Windows 本地) |
dsh-plugin-voxemw-cloud |
voxemw-cloud |
77K | 134 | 233 + 6 模块 + web/app.html |
云 API 面板 |
2.1 结构性结论(这是"必须整合"的真正理由)
它不是 7 个平行插件,而是「1 个宿主壳 + 6 个功能页」。
dsh-plugin-mcn注册了 25+ 条/mcn/api/*HTTP 路由、inject = ["slots","sessions"]、在sidebar.footer.action注册唯一入口,并提供两个全局扩展点:window.__mcnEntries—— 功能页注册表(工作台首屏图标网格读它渲染,源注释写明"外部插件注册的功能入口,即插即用")window.__mcnNav—— 页内路由(open(entryId, params)/openView(id)/back())
- 其余 6 个包全靠往
__mcnEntries注册 + 被__mcnNav.open()打开;三个 douyin 包的 host 面只有 9 行空壳,注释原文写着"数据复用dsh-plugin-mcn服务端注册的/mcn/api/*路由"。 - ⇒ 拆开的状态下"逐个启用"本身就违背设计:缺了 mcn,其余 5 个只是死链;反过来只启用 mcn 则功能残缺。整合是回归正确形态,不只是图省事。
- 附注:
dsh-plugin-mcn没有defineTool/tools.register(grep实证 = 0)→ 它是 UI + HTTP 插件,不给 agent 注册工具;agent 侧能力只来自技能与 prompt。
2.2 打包链路现状:不存在,要新建
7 个包均无 scripts 字段、无 build 脚本、无 package/ 目录、无 tgz 产物;lib/*.js 是直接维护的产物(client.js 顶部自带 var module = {exports:{}} 的 bundle 外壳)。→ 整合包必须新建打包脚本。
2.3 需要一并处置的残留
dsh-plugin-mcn/lib/ 内有 2 个 .bak(index.js.bak-20260906、index.js.bak3-20260906)→ 会被打进包、加重体积并可能触发上传扫描告警。docs/ui-check-home-fix/(1.6M)与插件无关,不进包。
三、范围
| 区 | 对象 | 动作 |
|---|---|---|
| 新建 | D:\dshworkspace\plugin_package\dsh-plugin-mcn-suite\(7 个原包原地保留作回滚参照) |
整合包源码 |
| 新建 | 整合包内 skills/(技能原文 + .manifest.json,来源 C:\Users\Administrator\.dsh\skills\mcn-short-video,11 MB / 711 文件) |
随包投放的业务技能,按 §4.1 取舍表挑选(不整包照搬) |
| 新建 | 打包脚本(scripts/build-mcn-suite.cjs 或等价) |
产出 dsh-plugin-mcn-suite-<ver>.tgz(tgz 内需 package/ 前缀,档案 25/28 教训) |
| 改 | 整合包内的 host/client 代码 | §五 的 P0 适配 |
| 平台侧 | 门户候选池 | 上传新包 + 下架旧 7 包(防并存,见 §七) |
| 文档 | 新建档案 + INDEX §二 + 03-路线图 + 交接单/README §一 |
归档 |
明确不动:❌ dsh 官方包与缓存(红线 R2)❌ 平台自建三 bundle(portal-entry / business-plugins / workspace-scoped-picker)❌ /opt/dsh 权限 ❌ 7 个原包源码(保留)❌ 不新增平台侧权限能力(不触发 R5)
四、决策点(已由用户 2026-09-12 拍板)
用户口径:「A 按照最佳方案处理;B 按照你的建议处理 —— 业务技能能否打包到插件中一起安装和使用,不要分开管理!;C、D 按照最优方案处理」
| # | 决策 | 定稿 |
|---|---|---|
| 1 | dsh-plugin-social-workbench 进不进包 |
✅ 不进包(采纳建议 A)。实测是 Windows 本地服务守护:EASEL_DIR 默认 D:\dshworkspace\Easel、可执行文件 .venv/Scripts/easel.exe、探针打 127.0.0.1:7860/:18789;平台实例里 ① 该 exe 不存在 ② 127.0.0.1 已被档案 39 封(实测 BLOCKED)→ 必然不可用。该入口在平台不投放,原包源码保留 |
| 2 | dsh-plugin-voxemw-cloud 进不进包 |
✅ 进包。凭据全程走 env(无硬编码 ✓、getPublicConfig 已剥离 apiKey ✓),平台实例无该 env → 入口显示「未配置」,不崩 |
| 3 | 整合后的入口形态 | ✅ A=保留 __mcnEntries 机制、多入口并列(工作台首屏图标网格),client 面行为与现状一致 |
| 4 | 业务技能投放 —— 用户明确要求 打进插件包、一起安装使用、不分开管理 | ✅ 改为「技能随包投放」,实现见 §4.1。这推翻了规划会话原先"走门户技能管理分开投"的建议,据此重设计 |
| 5 | 包名与版本 | dsh-plugin-mcn-suite v0.3.0。版本号必须每次递增:同名同版本 tgz 即使内容变了,pnpm 也复用旧包(档案 18 踩坑 1) |
4.1 技能随插件包投放(用户要求:不分开管理)
为什么可行:技能发现位在实例内 —— dsh discoverRoot 只扫 $DSH_HOME/skills(档案 41 §B 实测:启用/禁用靠目录 rename 即可即时生效、无需重启);而插件的 host 面代码就跑在实例内、以该用户 uid 运行 → 插件完全有能力自己把技能装到位,不需要平台技能管理面参与。
机制(建议,执行会话按此实现):整合包内置 skills/(技能原文)+ skills/.manifest.json(技能名 → 版本 / 文件数 / sha256)。host 面在每次实例启动调用 ensureSkills():
- 读
.manifest.json;对每个技能比对$DSH_HOME/skills/<name>/.installed.json记录的版本; - 幂等:版本一致 → 跳过(不重复写盘);
- 版本不一致 → 全量替换(先删后放,旧版已删除的文件一并清除 —— 与档案 11「apply 全量替换」同语义);
- 不覆盖用户自建的同名技能(目录存在但无
.installed.json)→ 跳过 + 写日志 + UI 提示"被同名技能占用"; - 写加载标记
[mcn-suite] skills ensured: [email protected], …(回归看日志即可,档案 26 §建议)。
备选(更省事,但必须先实测):若 dsh 的 skill 扫描认得目录软链,可改用软链 <DSH_HOME>/skills/<name> → <profile>/node_modules/dsh-plugin-mcn-suite/skills/<name>,升级插件即升级技能、零对账。风险:symlink 是否被扫描/读盘正常要实测(档案 39 的教训:symlink 行为必须 realpath 级实测,别假设)。→ 执行会话先试软链,不行再退回"复制 + 版本对账"。
投哪些 / 不投哪些 —— 按「引用驱动」裁剪,不按"看着像资料"裁(用户 2026-09-12 定「参考资料不需要」;规划会话随后实测引用关系,发现三类"像资料"的其实是被引用的功能件,故按下表执行):
| 内容 | 体积 | 引用实测 | 定稿 |
|---|---|---|---|
主技能 mcn-short-video(SKILL.md + references/ + references-add/ + scripts/ + 帮助文档.md) |
~1.3 MB | — | ✅ 投 |
子技能 mcn-dou-analysis / mcn-script-review / mcn-video-prompt / mcn-data-insight 主体 |
主体 | 插件 prompt 正是引用这 4 个 | ✅ 投 |
mcn-data-insight/ —— 它本身是"技能集索引",下挂 12 个二级子技能(douyin-top-account(140K) / douyin-account-diagnosis(136K) / douyin-rise-ranking(76K) / douyin-hot-trend(72K) / douyin-ai-feed / douyin-prohibited-word / playlet-douyin-feed / douyin-weekly-surge / douyin-works-crawler / douyin-content-surge / douyin-daily-hot / douyin-search,全部带 SKILL.md) |
869 KB | 插件的「榜单更新」任务依赖其中的 douyin-top-account;workshop.js 的赛道白名单同源 |
✅ 整块投(这是"技能树"的第二层,不是参考资料;find -maxdepth 容易漏它,核对时注意层级) |
mcn-dou-analysis/references/样例/(模板_旧梦留声机账号设定卡.svg 19 KB + 参考_旧梦留声机_账号设定卡_长图版.png 994 KB) |
992 KB | references/feature/06_生成账号设定卡片.md:67 写明"动手前必须先读"这两份 → 功能件,不是资料 |
✅ 保留。用户说的"参考资料不需要"不覆盖这一项;若你仍要剔,则必须同改那条指令,否则留下死引用 |
nuwa-skill-main/ 主体(去掉 examples/) |
104 KB / 11 文件 | 被引为"主方法论":feature/03_提炼账号设定.md:81、references/人设卡生成方法.md:186 |
✅ 保留 |
nuwa-skill-main/examples/(trump-perspective/ 英文研究文档等) |
≈ 2.4 MB | 人设卡生成方法.md:187-188 引了 examples/mrbeast-perspective/(未引 trump-perspective) |
❌ 剔。精确做法:只剔未被引的 trump-perspective/;若要连 mrbeast-perspective 一起剔,必须同改那两行引用 |
mcn-video-prompt/参考skills/(seedance2.0-prompt-skill、script-writing-studio 上游全文) |
2.0 MB | 实测 0 引用(grep 命中 0)→ 纯资料 |
❌ 剔 ✓ —— 用户"参考资料不需要"指的就是这类 |
browser-harness/(整包 Python + 自带 venv + docs/png) |
2.3 MB | 多篇 feature 要求"浏览器优先"(feature/01_获取账号信息.md:12、feature/02_获取视频.md:10)+ 主 SKILL.md 有专章(L88/105-112) |
❌ 剔目录,但必须同步改文档:写明「平台无浏览器(档案 38a 实测 Playwright 装不起来)→ 改走 MCP / 其它路径」,否则留下死引用,agent 会像档案 55 那样反复撞墙 |
scripts/mcp-config.json |
16 KB | 插件 MCP 依赖(功能必需部分 = URL) | ⚠️ 剔 MYAI_FEISHU_APP_ID(cli_a84d0a47f93e1013)、保留 URL(maiya-trans.youmanvideo.com);再按 §五#7 过扫描 |
裁剪后体积预期:11 MB → ≈4.3 MB(剔掉 6.7 MB:参考skills 2.0 + browser-harness 2.3 + nuwa/examples 2.4)。
⚠️ 投的是整棵技能树,不是一个技能:全树 36 个 SKILL.md / 711 文件(主技能 1 + 一级子技能 5 + mcn-data-insight 下 12 个二级子技能 + 更深层的参考技能目录)。核对层级时别用浅 find(规划会话首轮就是这么误判 douyin-top-account "缺失"的)。
裁剪必须机械校验(新增,防"剔完留死引用"):剔完跑一次死引用扫描 ——
grep -rIn -E "参考skills|browser-harness|nuwa-skill-main/examples/trump|参考_旧梦留声机" <整合包>/skills/ \
| grep -v -E "已移除|无浏览器|不再使用"
期望:只剩"已移除 / 已改走 X"这类说明行(即上面要求改写的指令),没有一条指向已删文件的活引用。
⚠️ 与共享技能层互斥:本单走"插件内投"后,不要再把 mcn-short-video 投到平台共享技能层(bundledSkillDir,档案 10/40)—— 两条通道同名会按 rank 抢,行为不可预测。详见 §七。
禁用 / 卸载语义(取舍,已定):插件被禁用(disabled: true)时不删已装技能(用户可能正在用),只在下次启用时按版本对账;卸载插件会留下技能残留 → 记为已知残留(清理走实例内「我的技能」或目录手工删)。
4.2 技能落地方式(装到哪)与撞名规则(2026-09-12 用户提问定稿)
问题一:装到 $DSH_HOME/skills/,还是"就在插件内目录使用"?
关键事实:dsh 的技能扫描根是一个固定集合(个人 ~/.dsh/skills rank300 / 项目级 .dsh/skills rank100 / .agents/skills rank200 / 平台注入的 bundledSkillDir,档案 10 §Skill 装载机制 / 档案 41 §B),插件包目录(profile/node_modules/<pkg>/skills/)不在其中。
⇒ "就在插件内目录使用"意味着 agent 无法按技能名加载(skill 机制发现不了),只能靠 prompt 给绝对路径让它自己读 SKILL.md。
| 方案 | 能否按技能名加载 | 副本/维护 | 备注 |
|---|---|---|---|
A 复制到 $DSH_HOME/skills/<name> |
✅ 能 | 每用户一份 ≈4.3 MB;需版本对账 | 最简单确定;用户能在「我的技能」看到(但归插件管) |
B 软链 $DSH_HOME/skills/<name> → 插件包内目录 |
✅ 能(前提:实测 dsh 跟随 symlink) | 零副本;升级插件即升级技能,零对账 | 副作用:用户在「我的技能」里删/禁 → 断链或删链,需处理;必须先实测(档案 39 教训:symlink 行为必须 realpath 级实测,不许假设) |
C 不落扫描根,prompt 给"包内绝对路径"(插件运行时用 import.meta.url 算出) |
❌ 不能 | 零副本零对账 | 兜底方案:agent 用不了 skill 名;用户不可见、不可管理;每次 prompt 都要注入路径 |
→ 定稿:B 优先(一条实测命令即可确认);B 不通退回 A;C 仅作最终兜底。
问题二:同名技能怎么处理(三层,按优先级)
| 场景 | 处理 | 理由 |
|---|---|---|
| 插件技能 ↔ 用户自建同名 | 让位:不覆盖,跳过 + 日志 + UI 提示"被你的同名技能占用" | 用户的东西优先;档案 41 已有"同名 409"的先例,不静默覆盖 |
| 插件技能 ↔ 平台共享技能层同名 | 互斥:本单不投共享层;若共享层已有同名,插件让位并提示 | 共享层 rank 更高会压住个人层,静默失败比明说更糟(§4.1 互斥条) |
| 插件技能 ↔ 另一个插件同名 | owner 标记 + 先到先得 + 冲突报错:每份由插件装的技能写 .installed.json(owner: <包名>@<版本>、skillVersion、sha256);安装前查目标目录 —— 无标记=用户自建→让位;owner=自己→按版本对账;owner=别的插件→拒绝写 + 明确提示 |
多插件共存时行为可预测、冲突可见;不建议给技能名加命名空间前缀(要改所有 prompt,且用户看到怪名字) |
判定顺序(实现时按此写):目标目录不存在 → 装;存在且 .installed.json.owner == 本包 → 比版本,不同则全量替换;存在但无 .installed.json → 用户自建,让位;存在且 owner == 其他包 → 拒绝 + 报错。
五、平台化 P0 清单(不修则"传上去也不能用")
| # | 问题 | 实测位置 | 改法 |
|---|---|---|---|
| 1 | homedir()/.dsh 硬编码 —— 平台实例的 DSH 家目录是 $DSH_HOME,不是 ~/.dsh |
mcn/lib/config.js:6,28,68;mcn/lib/index.js:733,771,865,881,935,964,978,1124,1167,1496,1565,1630,1969,2143,2840;mcn-schedule/lib/index.js:11,27,84 |
统一 const DSH_HOME = process.env.DSH_HOME → join(DSH_HOME, …)。注意三处子路径:storages/workspace.json、sessions/、profiles/,都在 DSH_HOME 下 |
| 2 | 技能路径硬编码 ~/.dsh/skills/mcn-short-video/subskills/<x>/ |
mcn/lib/index.js 多处:1255、1565、1798、1813、1834、2078、2313、2364、2491、2546、2984、2986… |
改成只报技能名(mcn-dou-analysis 等),由 agent 自行解析(档案 27 P0-2);技能随包投放(见 §4.1),prompt 里不再出现任何绝对路径 |
| 3 | spawn("powershell", …) 杀进程树 = Windows only |
mcn/lib/index.js:919(两个 .bak 同位置) |
平台是 Linux → 改 process.kill(-pid, …) / pkill -P,或直接删掉该清理分支(评估残留影响) |
| 4 | MCP spawn(npx -y myai-mcp) 每次现拉包 |
mcn/lib/mcp.js:21、config.js:14 |
平台侧预装成包(档案 27 P1),并确认实例内 npm registry 可达(档案 38a:仅云元数据端点被封,其余放行但只记日志) |
| 5 | .bak 混在 lib/ |
mcn/lib/index.js.bak-20260906、index.js.bak3-20260906 |
打包前剔除(构建脚本过滤 + files 白名单) |
| 6 | 上传扫描可能误伤(P0 阻断 / P1 告警) | 整包 | 打包后先本地 dry-run src/web/security-scan.ts 的规则(P0:/etc/shadow、base64 解码执行、nc -e、/dev/tcp 反弹、fork 炸弹、给系统二进制加 setuid;P1:child_process、vm/eval/new Function、敏感 env、超长 base64、外部网络、隐藏目录)。已知 child_process(mcn spawn、social-workbench spawn)只会触发 P1 告警、不阻断,但要确认整包 P0 = 0。注意技能原文里有大量代码示例/脚本,P0 风险比插件本身高 |
| 7 | 技能原文里的凭据 / 平台情报(随包投放新增) | mcn-short-video/scripts/mcp-config.json(实测含 MYAI_FEISHU_APP_ID=cli_a84d0a47f93e1013 + 私域 URL maiya-trans.youmanvideo.com)、scripts/MCN_CYLG_API.py(命中 key/token 模式,需逐个核对)、subskills/browser-harness/.env.example |
入包前对 skills/ 整目录做凭据扫描:`grep -riE "api[_-]?key |
| 8 | 插件 prompt 里的技能路径层级不符(2026-09-12 复核,更正规划会话先前的误判) | 插件 prompt 写的是 subskills/douyin-top-account(mcn/lib/index.js:3085、3086;mcn/lib/workshop.js:9,22),真实路径是 subskills/mcn-data-insight/subskills/douyin-top-account/(少了一层)。技能本身完整存在(36 个 SKILL.md / 711 文件)—— 规划会话首轮的"缺失"结论是搜索深度不够(-maxdepth 5 差一层)导致的误判,已更正 |
无需补文件;并入 #2 一起修:所有 prompt 改成只报技能名(douyin-top-account),由 agent 自己解析 → 层级问题自然消失。打包前必跑:grep -rn "subskills/" lib/ 逐条核对是否与技能实际层级一致 |
六、步骤(每步自带验证)
- 前置只读核查:
git -C <平台代码> log -1(取当前 HEAD 备写档案)/ls /opt/dsh/artifacts/(看版本命名与package/前缀)/ 门户#/plugins候选池现有 7 包的实际状态(是否有人已启用)。 验证:拿到"候选池里有哪几个、谁在用"的事实清单。 - 决策与技能完整性:§四 五个决策点已定稿;另需向用户确认 §五#8 ——
douyin-top-account子技能的去向(补进包 / 已废弃改 prompt / 走 MCP)。该项未确认前不要打包。 - 建包骨架:
package.json(namedsh-plugin-mcn-suite、version、type: module、exports {".", "./client"}、dsh.bundle.patch、dsh.client.inject= mcn 的[runtime, sidebar]并集)+cordis.patch.yml(只 1 条insert:id: mcn-suite)+lib/{index.js, client.js, host/*}。 验证:node --check全通过;cordis.patch.yml里 insert 条目数 = 1(防 duplicate,档案 25)。 - 移植 host 面:把 mcn 的 9 个模块 + mcn-schedule + voxemw(+决策 1 的 social-workbench) 收进
lib/host/,保持路由前缀不变(/mcn/api/*、/voxemw/api/*)→ client 面零改动。 验证:路由清单 diff:改前 25+ 条 vs 改后逐条一致(只增不减)。 - 合并 client 面:一个
client.js内注册全部__mcnEntries条目;inject取并集;确认无exports.default(红线 R3,实测 7 个原包全部合规 ✓,合并时别引入)。 验证:grep -c "exports.default"= 0。 - P0 适配(§五 1–5)+ 按需加加载标记:
console.log("[mcn-suite] loaded …")(把回归从"看 UI"变成"看日志",档案 26 §建议)。 验证:grep -n "homedir()" lib/**/*.js= 0;grep -n "powershell"= 0;grep -rn "\.dsh/skills"= 0。 - 本地 smoke:参考
dsh-plugin-voxemw-cloud/tests/smoke.mjs的写法,跑一遍 host 面(路由注册、DB 建表幂等、无 env 时降级不崩)。 验证:smoke 全绿;无 env 情况下进程不抛。 - 打包:产出
dist/dsh-plugin-mcn-suite-<ver>.tgz,内含package/前缀,不含.bak/node_modules。 验证:tar tzf清单人工核对(顶层只有package/);体积与du对照;md5sum记档。 - 扫描 dry-run:按 §五#6 规则跑一遍 → P0 必须为 0;P1 告警逐条判断是否误报。
- 上传:admin 门户
#/plugins上传 tgz → 进候选池(默认禁用)。产物不要复制进用户 ws(档案 28 平台污染教训)。 验证:候选池列表出现该包 + 版本号正确。 - 切换(关键,防并存):① 先在候选池下架旧 7 包 ② 用户实例「功能管理」启用新包 ③ 确认弹窗 → 重启实例 ④ 走
restartAndProbe探活(档案 34)。 验证:§八 C–F。 - 实测验收 + 资源:逐功能页点开;用
scripts/probe-instance-mem.cjs量私有内存与启动时间(档案 58 预算:MemoryMax 384 MiB/ 堆 256)。 验证:§八 D、G。 - 归档:新建档案(原子占号
mkdir 04-调整方案/.lock-<NN>)→ 更新INDEX §二/03-路线图/交接单/README §一→ 本单git mv到archive/交接单-已完成/。 验证:docs-audit.py退出码 0。
6.1 技能随包的额外步骤(并入上面第 3、6、8、10 步,不另起编号)
- 建包阶段(并入第 3 步):按 §4.1 引用驱动裁剪(不是"看着像资料就删")→ 放整合包
skills/<name>/;生成skills/.manifest.json(名称 / 版本 / 文件数 / sha256);剔完跑 §4.1 的死引用扫描。 验证:du -sh skills/≈ 4.3 MB(而不是 11 MB);tar tzf确认没有参考skills/、browser-harness/、nuwa-skill-main/examples/trump-perspective/;确认有mcn-dou-analysis/references/样例/(含 994 KB 长图)、nuwa-skill-main/SKILL.md、mcn-data-insight/subskills/douyin-top-account/(这三块都是被引用的功能件,不能剔)。 - 改码阶段(并入第 6 步):实现
ensureSkills()(§4.1 五步)+ 加载标记;把mcn/lib/index.js的 13+ 处技能绝对路径改成技能名。 验证:grep -rn "\.dsh/skills" lib/= 0;grep -rc "ensureSkills" lib/≥ 1。 - 打包阶段(并入第 8 步):
skills/整体进包(package.json的files白名单必须包含skills)。 验证:tar tzf dist/*.tgz | grep -c "package/skills/"≈ 技能文件总数(数百)。 - 上线阶段(并入第 10 步):上传 → 用户启用 → 重启 → 按 §八 J/K 验技能就位。
6.2 收尾顺带(D 组:两处文档漂移 —— 用户已定「并进 T03 收尾」)
BRIEF.md:20:scope(512M / 150% / TasksMax 128)→384M,并补NODE_OPTIONS=--max-old-space-size=256与NODE_COMPILE_CACHE(档案 58 实测)。03-路线图与待办.md:头部仍是 09-11 21:20 合并版、档案 57-61 完全未登记 → 补登记 + 刷新头部状态行;BRIEF §3待办同步。 验证:grep -n "384M" BRIEF.md命中;grep -nE "档案 (60|61)" 03-路线图与待办.md命中(写成(60|61)而不是字符类:档案\s*6[01]这种写法会被docs-audit.py的引用正则匹配成裸旧号6→ 永久悬空,T02 已踩过一次)。
七、风险与红线
🔴 头号风险:新旧包并存 → 实例崩
若某用户同时在位"旧 7 包"与"新整合包",会出现:/mcn/api/* 路由重复注册、sidebar.footer.action 重复注册、__mcnEntries 重复条目;严重时 duplicate loader entry id → 崩溃循环(档案 25 有真实前例,靠档案 20 熔断才拦住,但用户会看到死页面)。四道防线必须全做:
- 新包用全新 loader id(不复用
mcn-nav等 7 个旧 id); - 门户候选池下架旧 7 包(或标 deprecated,并在包描述写明替代关系);
- 切换按 §六 步骤 11 的顺序执行(先禁用旧 → 再启用新);
- 新包 host 面加防御:启动时检测旧 id 是否同时在位 → 命中则拒绝启动并写明确日志(宁可这个包不工作,也不要崩实例)。
其余
- 内存/启动(档案 58):合并后单实例加载模块更多,必须实测,超预算就按需懒加载或砍 social-workbench。
- 网络(档案 38a):实例出网只记日志不拦(元数据端点已封)→ MCP
npx、voxemw 云调用可达性要用实测确认,别假设。 - 技能同名互斥(§4.1):插件投的技能、平台共享技能层、用户自建技能三方可能同名 → ① 本单不投共享技能层;② 用户自建同名时插件跳过并提示;③ 若日后要改投共享层,必须先撤掉插件内投放。
- 技能随包的体积成本:约 1.5–2 MB × 每用户一份(用户已明确"不分开管理",接受该冗余);日后要省可改软链方案(§4.1 备选)。
- 红线:R2 不改官方 dsh 包(只走 bundle + patch)✓;R3 client 禁
exports.default✓;R5 本单不扩大平台权限 ✓;不自动升级 dsh ✓;未获明确授权不 commit / push。
八、验收
| # | 命令 / 动作 | 期望 |
|---|---|---|
| A | tar tzf dist/*.tgz | head ;md5sum |
顶层只有 package/;无 .bak、无 node_modules;md5 记档 |
| B | 上传扫描 dry-run | P0 = 0;P1 逐条有结论 |
| C | 门户 #/plugins 上传 |
进入候选池、默认禁用、版本号正确 |
| D | 全新用户:启用 → 确认弹窗 → 重启 | 侧边栏入口出现;工作台首屏功能入口逐个可打开 |
| E | journalctl -u dshs --since '10 min ago' | grep -E "mcn-suite|duplicate" |
有 [mcn-suite] loaded;无 duplicate loader entry id;无崩溃重启 |
| F | 启用探活(档案 34) | 探活通过;失败时快照回滚不误伤好插件 |
| G | scripts/probe-instance-mem.cjs |
私有内存 + 启动时间在档案 58 预算内(写实测值) |
| H | 候选池 | 旧 7 包已下架/标 deprecated;不存在"新旧同时可启用"的状态 |
| I | 技能随包就位(§4.1) | 全新用户启用 + 重启后:ls $DSH_HOME/skills/ 出现 mcn-short-video 与 4 个业务子技能;journalctl 有 [mcn-suite] skills ensured: … |
| J | 技能可用(agent 侧) | 实例内让 agent 按技能名加载(如「调用 mcn-dou-analysis」)→ 能解析到,无需给绝对路径 |
| K | 幂等 / 不覆盖 | 再重启一次 → 技能不被重复写(.installed.json 版本一致则跳过);人为放一个同名自建技能 → 插件跳过并提示,不覆盖 |
| L | 凭据(§五#7) | skills/ 凭据扫描:真密钥 0 命中;app id / 私域域名的处置有明确结论并写进档案 |
| M | D 组文档(§6.2) | BRIEF.md 已更正为 384M;档案 57-61 已在 03-路线图 登记 |
| N | 裁剪后引用完整性(§4.1 死引用扫描) | 只剩「已移除 / 已改走 X」的说明行;无一条指向已删文件的活引用(browser-harness 相关的"浏览器优先"指令必须已改写) |
| O | 包体积与技能树 | skills/ ≈ 4.3 MB(已剔 6.7 MB);含全部 36 个 SKILL.md(主技能 + 5 一级子技能 + mcn-data-insight 12 个二级子技能);保留了 references/样例/(992 KB)、nuwa-skill-main/SKILL.md、mcn-data-insight/subskills/douyin-top-account/ |
| P | 技能落地方式(§4.2) | 按 B(软链)先实测;不通则用 A(复制)。两种都必须能被 skill 机制按名加载(不是靠 prompt 给绝对路径) |
| Q | 撞名处理(§4.2) | 用户自建同名 → 让位并提示 ✅;平台共享层同名 → 互斥并提示 ✅;别的插件已占 → 拒绝写并报出 owner ✅(验证:人为造这三种场景各跑一次) |
九、回滚
- 包级:候选池删新包 → 重新上架旧 7 包 → 用户逐个启用 → 重启(这是为什么原包源码与 tgz 都要留档)。
- 实例级:走档案 34 的
restartAndProbe快照回滚;或直接禁用新包重启。 - 本单不改平台代码 → 无平台侧回滚;若为铺开改了
ensure-*脚本,按.bak-<ts>还原 + 重启。
十、回报格式(执行会话填)
## T03 执行回报
- 决策 1–5 选定:<逐条,谁确认的>
- 整合包:name/version = ? 体积 = ? 路由数 25+N 入口数 = ?
- P0 适配:homedir() 残留 0 ✅ / powershell 残留 0 ✅ / .dsh/skills 硬编码 0 ✅ / .bak 已剔 ✅
- 打包与扫描:tgz md5 = ? P0 = 0 P1 = ?(逐条结论)
- 上传与切换:候选池版本 ? 旧包下架 = 是/否 启用+重启 ✅ 探活 ✅
- 实测:私有内存 ? MiB / 启动 ? s(对照档案 58 预算)
- 验收:A–I 逐项 ✅(未过项写现象)
- 证据:<journald loaded 标记 + duplicate 检查输出 + 内存采样>
- 偏离:<…;无则写"无">
十一、不在本单
- 业务技能内容本身的维护/改版(只做"投放到位")
- voxemw 云链路接线(档案 12 的 M1 后段)
- Easel / social-workbench 的平台化改造(决策 1 选 C 才需要,另立单)
- 平台「功能管理」分区的其它 UI 调整(档案 57/60 已闭环)
九、验收结果(2026-09-13 09:5x · 只读取证,服务器实测)
口径:§八 逐项核对;能只读证的全跑了,需要浏览器/造场景的明确标出。
| # | 项 | 结果 | 实测证据 |
|---|---|---|---|
| A | 包结构 | ✅ | 池内 tgz md5 2ff1a2fa775891085949d50452394fa0 / 1,953,267 B / v0.3.9;顶层仅 package/;node_modules 0、.bak 0 |
| B | 上传扫描 | ✅(P0) | [upload-scan] blocked=0 warnings=118(warnings 逐条结论未做) |
| C | 候选池上传 | ✅ | 池内 dsh-plugin-mcn-suite @0.3.9(09-13 00:02) |
| D | 启用→重启→入口 | ⚠️ 未验(需浏览器逐个点开入口) | — |
| E | 加载日志 | ✅ | [mcn-suite] loaded v0.3.0(host 面 3 个:mcn, mcn-schedule, voxemw-cloud);duplicate loader entry id = 0;同期无崩溃 |
| F | 启用探活 | ⚠️ 未单独验(但平台已自动回滚过一次 ⇒ 该链路是活的,见 07:42–07:46 事故) | — |
| G | 内存 | ⚠️ 偏紧 | guest 峰值 382 / 上限 384 MiB;admin 峰值 321 / 384(档案 74 采样) |
| H | 旧 7 包下架 | ✅ | 池内只剩 mcn-suite + univer-office 两个;无新旧并存 |
| I | 技能随包就位 | ✅ | 落点 = <home>/skills/mcn-short-video(4.6 MB,含 4 个一级子技能);journal [mcn-suite] skills ensured … |
| J | agent 按技能名加载 | ⚠️ 未验(需实例内跑一次 agent) | — |
| K | 幂等/不覆盖 | ✅ | skills ensured: 跳过 [email protected](版本一致) |
| L | 凭据扫描 | ⚠️ 未验 | — |
| M | D 组文档 | ✅ | BRIEF.md 已是 384M(档案 58 收尾时改) |
| N | 裁剪后引用完整性 | ⚠️ 未验(需跑死引用扫描) | — |
| O | 包体积与技能树 | ⚠️ 口径待核 | 实测 4.4 MB / 18 个 SKILL.md(主 1 + 一级 4 + mcn-data-insight 下 13);§八 原文期望 36 个 SKILL.md ⇒ 差异需确认为"包被裁剪"还是"口径过时" |
| P/Q | 软链落地 / 三种撞名场景 | ⚠️ 未验(需造场景) | — |
结论:主体已通过(8 项实证);未验的 6 项(D/J/L/N/P·Q)都属"需浏览器或需造场景",可以另找时间补,不阻塞投放。
⚠️ 一条必须记的事实(guest 那次尝试):2026-09-13 07:42:52 guest 在「功能管理」提交启用 →
实例 07:43–07:45 连崩 5 次(exitCode=134,栈 = V8 JsonParser/HeapAllocator ⇒ V8 堆打满)→ 07:46 熔断 →
平台自动回滚(package.json.bak-rollback-20260913T0748)⇒ 现在 guest bundles 里没有 mcn-suite。
根因指向 --max-old-space-size=160 太紧(档案 74)+ guest cgroup 峰值贴顶 ⇒ 详见 04-调整方案/74 文末「现场反证」。
admin 侧自 09-13 00:02 起一直启用且各项实证通过(上表 A/E/I/K 均取自 admin)。
十、收尾记录(2026-09-13 18:0x)
本轮补齐的 gap
| 项 | 结果 |
|---|---|
| guest 启用(原「剩余」第 1 条,单子长期卡点) | ✅ 已完成 —— POST /api/plugins/mine/apply {id: dsh-plugin-mcn-suite, enabled: true} → 任务 c20739a6dbe8975c → status=success / stage=完成 / restarted=true;启用后 bundles 含 dsh-plugin-mcn-suite ✓ |
| 容量前置(07:4x 崩溃的根因) | ✅ 已解除 —— 档案 79 两处 P0 已修;R1-④ 动态配额生效(guest 装前 672 MiB → 装后 800 MiB)⇒ 「V8 堆打满 abort → 自动回滚」不再复现 |
| §八 L 凭据扫描 | ✅ 通过 —— ak_+32hex 0、sk- 0、app_id= 0、*.alotbuy.com 0;125 处命中均为变量名/文档说明;唯一 32+hex 是 .manifest.json 的文件哈希 |
| §八 O 包体积与技能树 | ✅ 通过(口径澄清) —— SKILL.md 实测 18 个;§八 原文期望「36」系笔误(主 1 + 一级 4 + mcn-data-insight 下二级 13 = 18,与文字描述完全吻合)。包总 4.27 MB / skills 3.55 MB |
| §八 P 技能落地形态 | ✅ 通过 —— admin 实例 skills/mcn-short-video = 实体目录(18 个 SKILL.md / 4.6 MB)⇒ 走 §4.2 的 A(复制)兜底路径 |
| §八 N 死引用扫描 | ⚠️ 未定判 —— 自动扫描出 63 处「包内路径对不上」,但抽样 8 个里 5 个是误报(引用省略 references/ 前缀,文件其实在);内容基本齐全(16 个 scripts/ 目录、27 个 .py、263 个 .md,知识库 12 类齐全)⇒ 不能判不合格,需人工逐条读;不阻塞投放 |
| §八 D / J / Q | ⏸ 未验(需浏览器或造场景);按 §九 口径 不阻塞投放 |
归档
- 本单主体目标已达成 ⇒ 按流程移入
archive/交接单-已完成/,并在INDEX.md §四与交接单/README.md §一、03-路线图与待办.md §二三处同步状态。 - 遗留(可选,不阻塞):D(实例内入口逐个点开)/ J(agent 按技能名加载)/ Q(三种撞名场景)/ N(死引用逐条人工判定)。