JHboy203/dsh-plugin-installer ↗★ 0

dsh-plugin-installer

DSH 侧边栏「插件/Skill 安装面板」:把 GitHub 链接、Release、.tgz/.zip 直链或本地压缩包丢进来,宿主侧确定性完成下载 → 解包 → 识别 → 安全预检 → 安装 → 快照/回滚 → 诊断;安装执行零 token,AI 只能推荐与提交请求。

包名
dsh-plugin-installer
兼容性
待验证
Harness 依赖范围
>=0.2.1-rc.1 <0.3.0
版本
0.2.1
许可证
MIT
最近更新
2026年10月27日

同名包的其他仓库

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:JHboy203/dsh-plugin-installer

dsh-plugin-installer · 插件 / Skill 安装面板

English

状态:开发版,非最终版本。 这是「边用边改」放出来的版本:功能与界面会不定期更新, 安装流水线、配置项与数据目录都可能变。装之前请确认下一节的 DSH 版本区间。

Status: development build — not a final release. Released as-is from daily use; features and UI change from time to time (the install pipeline, settings and the data directory may change too). Check the DSH version range below before installing.

插件 / Skill 安装面板

装完在哪打开:重启 DSH 之后,左侧栏会出现一个方块里带向下箭头的图标(文案「插件安装」, 排在你其它面板图标之间)。点它,右边就切成这个面板。 找不到图标时:侧栏底部「设置 → 插件」里搜 dsh-plugin-installer,确认它是启用状态; 插件树是按启动期读的,所以装完必须重启一次 DSH。 想在任意页面按一下就自检:Ctrl+Alt+D(设置里可改键位或关掉)。

侧边栏一个图标,点开是「插件 / Skill 安装」面板:把 GitHub 链接、Release 页、.tgz/.zip 直链,或者本机压缩包/目录丢进去,面板自己完成 下载 → 解包 → 识别 → 安全预检 → 快照 → 安装 → 诊断 →(按需)回滚。

安装执行全程不调用模型:确定性代码在宿主侧跑,AI 只做「推荐」和「递请求」。 本机实测一次 Skill 安装 约 1.8 秒、插件安装到确认前 约 0.6 秒,都不花 token。

R12 起它也回答「我装了哪些、哪些能升级」:面板里多一张「已装插件」卡(默认折叠), 按 profile 的四类来源分开标(npm / 本机 link: 目录 / 本面板重打包的 .tgz / Git), 点「检查更新」才联网比对 npm 最新版 —— 本机实测 17 个可比对的包里 7 个可升级, 每一行都能直接点「升级到 x.y.z」走原流水线(预检 / 快照 / 诊断 / 回滚一个不少)。

侧栏图标(红点在这里) ─┬─→ 点开 = main 面板
                        └─ 红点只在「装失败 / 等你确认 / 诊断有冲突」时亮

1. 它在哪、怎么用

R2(2026-10-20)新增三件事,都不改九步流水线:

  1. 诊断卡默认就写「怎么解决」:每条发现带 fix: [{text, command?}] 步骤清单,命令可一键复制;
  2. 「大白话解释」开关(用户指定的名字,别再改回「通俗讲给我听」):卡片右上角, 存 localStorage 的 dpi.plain-explain;打开后把术语换成大白话,原文收进「术语原文」折叠。 诊断卡本身永远先给一句大白话结论(diagnosis.plain)。
  3. 7z / rar 现在能装了:借 Windows 自带的 %SystemRoot%\System32\tar.exe(bsdtar + libarchive 3.8,本机实测能造能解 7z),解完再按实际落盘的东西复核围栏 (链接/数量/单文件/总量/压缩比),因为 bsdtar 自己不管这些。zip64 与少见存储方法 (deflate64 等)也走同一个兜底。

自检从 92 → 111 项(新增:plain/fix 数据契约、client 静态契约、7z 往返 + 围栏)。 7z 那组要 spawn 子进程,只能在宿主进程里跑(见 §7 的 host_selfcheck),沙箱里会 EPERM。

R3(2026-10-20 04:00)再加两件(宿主侧代码,重启生效):

  1. 搜索从 2 个源扩到 6 个源(npm 两串 / GitHub 两串 / topic 发现 / dsh-hub.cc 商店目录), 每条命中带 trust(官方、许可证、有源码、近期更新 + 红旗:无仓库、已归档、刷星、 年轻但星极多)与 popularity(npm 周下载量、商店安装量/评分、stars、本机评分)。 见 lib/sources.js。
  2. 按用途判断权限是否合理(lib/purpose.js):推断用途档(界面/网络/媒体/运维/数据/Skill) → 与扫描到的能力对账 → ok / warn / error / unknown + 大白话 + 建议;预检卡新增 「用途 vs 权限」块。eval/vm/new Function/混淆 在任何用途里都算 error;Skill 里带可执行文件 直接 error;用途认不出来就不判。

自检 111 → 132;无头渲染 12 → 18;新增只读探针 dev/probe-repo.mjs (下载→解包→扫描→预检+用途判定,不安装)。

R4(2026-10-20 13:10)把「花多少 token」变成可测的:确定性步骤全部 0 token, 唯一模型调用是异步 AI 审查(实测材料 7,055 字符 ≈ 2.5k 输入,封顶 ≈4k); 新增 reviewWorthIt()(只有安装脚本/提示注入/高危命中/用途不符才审,否则跳过并写明理由)、 estimateReviewTokens()(面板显示「≈2610 token」或「省下 ≈2650 token」); 并收紧扫描规则:obfuscation 只认长 base64 字面量、native 不再误吃 'egress.node', 每条命中带 why。自检 132 → 152,渲染 18 → 22。

R5(2026-10-20 18:40)修三件「自检查不到」的事(宿主侧,重启生效):

  1. 命中密度不再被丢掉。scanTree 一直同时算了 counts(样本,每类最多 5 条)和真实分布, 但摘要只读前者 —— 实测 dsh-our-free-model 面板上全是「×5」,真实是 74 个文件都在发网络请求。 现在新增 density(每类能力出现在几个文件里,跨文件精确、零额外成本),摘要改用它, 面板显示「网络请求(74 个文件)」;purpose 的发现也带「N 个文件里都有」。 同时加一条 bundle-payload 组合信号:同类命中铺满几十个文件时折成一条, 明说「这是把依赖/bundle 一起提交的仓库,按发布者可信度判断,别逐条数命中」。
  2. 搜索加了 TTL 缓存(memoizeTtl:同 key 5 分钟一次、并发共享、抛错不缓存; npm 下载量 30 分钟)。原因很实:一次搜索固定 6 个源 + 最多 10 个下载量请求,而 GitHub 搜索 API 未认证只有 10 次/分(一次搜索用 3 次)→ 一分钟内第 4 次搜索必 429。 撞限流时输出里直接给「设 DSH_GITHUB_TOKEN 可提到 30 次/分」的处置。
  3. 文案与实现对齐:工具数 4 → 5(启动日志按实际数量打印)、install_hub_search_plugins 描述写明 6 个源(含 dsh-hub 商店评分/安装量)、AI 推荐提示词说明候选来自商店、 删掉 download.js 里已无调用方的旧 searchNpm/searchGitHub。

自检 152 → 172,渲染 22 项不变。

R5.1(2026-10-20 19:05)把 R5 列出的四个缺陷修掉(宿主侧,重启生效):

  1. 构建标记:buildStamp() = package 版本 + 源码(index.js / client.js / lib/*.js)里最新 mtime → /health 的 build 不再是写死的 "p2-pack",/state 的 host.build、 install_hub_status 第二行、环境体检卡都能看到。client.js 另加 BUILD 号随 client-state 上报。
  2. 「等待确认」的现场有保鲜:prune() 现在用 stagingKeepNames(pkg, jobs) —— 没跑完的任务(queued/running/awaiting-confirm)引用的 staging/uploads// 与 staging// 不再被 24 小时 mtime 清掉(以前跨夜 + 重启 = lost-state)。
  3. 升级 / 降级 / 同版本重装说清楚:预检读 profile/node_modules//package.json, 新增理由「这是升级:已装 0.0.5 → 本次 0.1.0」,降级给 warn。
  4. 商店源用满:shapeHub 展示「分类 · 界面」徽章、安装量 why 带评论数/点装数, hub 搜索取 min(20, per*2) 条再本地重排。

自检 172 → 194(+build 4、+prune 4、+installed 6、+hub 4、+client/index 静态契约 4)。

R6(2026-10-20 19:30)面板「设置」——个性化,但不放松判据。右上角「设置」:

  1. 提醒:侧栏红点(默认开)、声音(默认关,WebAudio 一短音)、桌面弹窗(默认关, Notification;未授权先问一次)。红点关掉后 attention.dot=false,但 count/items 照常给。
  2. 审核三档:严格(检出任何能力特征就停 + 低/中危也 AI 审一次)→ 普通(默认 = 现有强度) → 关闭(不停、不调模型,但 profile/未识别/版本不兼容 照样阻断)。
  3. 「跳过审核」按钮(默认开):只在「等待确认 + 这次本来要审」时出现;点击后原地二次确认 (写明不停下 / 不花 token / 风险提示仍有效 / 可回滚),宿主留痕 reviewSkipped 与日志。
  4. 其余:轮询 2.5/5/10s、历史 5/8/20 条、快照 20/40/80 份、搜索源 npm/GitHub/商店(至少留一个)。 落盘 data/settings.json,默认档 = 标准版;写坏了会被归一化夹取回默认。

自检 194 → 245(+设置 23、审核三档 9、红点与跳过审核 7、搜索源 3、路由/客户端契约 9); 渲染 22 → 42(真渲染设置面板两轮 + 跳过审核按钮三态)。

R7(2026-10-20 19:20)自检热键 Ctrl+Alt+D(纯客户端):任意页面按一下就跑一次环境自检 (复用宿主 action:"repair",不新开一套判据),结果用游离浮窗报(面板没挂载也看得见), 浮窗自己消失、热重载先摘旧监听(否则一次按键发 N 个请求);键位存 dpi.selfcheck.hotkey, 可在设置里改 Ctrl+Shift+D / Alt+Shift+D / 关闭;有任务在跑时拒绝动手,宿主 doctor(apply) 也有并发闸。自检 +15 → 260。

R8(2026-10-20 19:40)把「装完就起不来」的判据并进面板(宿主侧,重启生效):

  1. 预检第 ⑤ 项:工具注册契约(lib/contract.js)。DSH 0.2.1 起 ctx.tools.register() 要求 output { schema, render },漏了不是静默降级 —— 插件激活失败;required 插件还会让 整次启动中止(症状:恢复对话框 + 一串「等待服务」的插件,2026-10-18 就这么挂的)。 现在装之前扫解包目录(precheck 给 warn + 两条处置步骤),装之后 diagnose 再扫一遍已装的目录,直接进诊断卡。
  2. 环境体检扩成 7 类(lib/profilehealth.js):dsh.profile.bundles 与 node_modules 对账 (被「禁用第三方插件」按钮裁到 2 项也认)、已装插件的 output 契约、pnpm 的 dsh-tools 钉子是否 随宿主升级过期、最近一次启动失败(crash-*-host.log 宿主崩溃 / crash-*-web-boot.log 前端入口 没激活,R17 起两类都读)点名了哪些插件、属于已知签名里的哪一类。apply:true 除原有两项外, 还会把钉子同步到宿主版本、从最近快照写回 bundles/patch(写前自动再存一份快照)。
  3. 一次调用看全:install_hub_doctor 默认只回「一行结论 + 每条发现一行」,verbose:true 才带 detail;contract:false 可跳过已装插件扫描提速。换会话 / 装完 / 起不来时只需这一个工具。
  4. 自检 260 → 269(+契约扫描 4、profile 体检与修复 5)。

R6.1(2026-10-20 22:30)把「怎么验收」固化成 dev/audit.mjs(收尾轮): 28 项自洽检查 —— 默认档冻结快照(改 DEFAULTS 而不改承诺会红)/ 5 个工具的 output 契约 / 构建标记格式(0.1.0 · YYYY-MM-DD HH:MM,且 /health 不许写死)/ 客户端设置与热键入口 / settings.json 归一化后逐字不变 / 等待确认任务的 staging 现场还在 / README 与交接文件里的 断言数是否等于 EXPECTED_ASSERTIONS。不碰宿主、不 spawn、不写盘,只打印 「接着跑 host_selfcheck(885)+ render-check(153)」。自洽检查现已 177 项。 三层分工至此固定:audit = 自洽,host_selfcheck = 行为,render-check = 界面。 线上实测:构建标签 0.1.0 · 2026-10-20 19:45、客户端 r6.0、GET/POST /settings 正常 (非法值当场夹取回默认)、clientReport 里有 360×658 的窄屏实例。

R9(2026-10-22 01:15)「装不上」时说清为什么、并给出真能走的一步(宿主侧;实测被运行中的宿主重新 apply,未重启即生效,但别把它当保证):

  1. 失败分诊(新 lib/installfailure.js):把宿主自己的两条判据搬到面板 —— ① pnpm 失败的 10 类(build-blocked / not-found / no-matching-version / disk-full / permission / integrity / network / timeout / pnpm-missing / unknown,次序与正则 照抄 dsh-plugin-manager 的 LOG_KINDS);② ManagementFailure 的 11 个拒绝码 (incompatible-version / already-installed / ambiguous-install / not-bundle / invalid-spec / stale-approval / management-required / stop-profile / bundle-in-use / not-removable / unaddressable)。每条都给「为什么(大白话)+ 建议 + 2–3 条处置步骤」, 原始输出留 4 KB。以前:只有 pnpm 的英文原文;宿主拒绝时甚至只有一行 incompatible-version。
  2. 安装脚本待批准的完整闭环:pnpm 拒绝执行构建脚本时,宿主机把包名以哨兵 "set this to true or false" 写进 profile 的 pnpm-workspace.yaml → allowBuilds 然后失败。 面板现在:读回名单(parseAllowBuilds,不引 YAML 依赖)→ 卡片逐个列名 + 二次确认 → 经 installBundle(spec, { approvedBuilds }) 转交(宿主自己写 allowBuilds=true 再跑 pnpm; 名单过期它抛 stale-approval,我们翻成人话)。复核在宿主侧:只接受仍然 pending 的包名, 面板不提供「一键放宽权限」。
  3. 模型拿不到这个动作:approve-builds 只在面板路由里,五个工具都没有它 —— 「让作者写的脚本在本机执行」只能由人来按。
  4. 体检多一条:profile 里还挂着待批准脚本(跨任务残留,会挡住下一次装同一个依赖),只报不修。
  5. 顺手修掉一个真 bug(线上撞出来的):save() 用固定 state.json.tmp, 而「action 响应里顺带跑的 snapshotState() → ensureReconciled() → save()」会和新任务的 execute() 撞在同一个 tmp 上 —— 先完成的那次 rename 走了文件,后一次报 ENOENT: rename 'state.json.tmp' -> 'state.json',而这条错误会被当成安装失败记在任务上。 现在:save 串行 + 每次唯一 tmp 名 + rename 对瞬时错误(ENOENT/EPERM/EACCES/EBUSY)重试; settings.json 同样处理。实测证据:修前那条重试任务报 ENOENT,修后同一条重试报出真实原因 (「认不出这是压缩包」)。
  6. 自检 269 → 372(+103:分诊 10 / 文案与拒绝码 14 / 批准闭环 20 / 落盘竞态 3 / 豁免与兼容 44 / 接线与暴露面 12), 无头渲染 42 → 62(+20:批准卡两态 + 兼容卡三态),自洽 28 → 52。

R10(2026-10-22 01:45)「版本不兼容」从死路变成一条说得清的路(宿主侧 + 面板):

  1. 面板不认宿主已有的豁免 → 死循环(真 bug):判定兼容性时没把 profile 的 compatibility.json 传进 evaluatePluginCompatibility,于是「已经授权过的版本」 回来装还是被红牌挡住,而卡片给的下一步是「先退出 DSH 再跑 CLI」。现在:读记录 (lib/exemptions.js,宿主接口优先、文件兜底)+ 认证传参,授权过的版本按 「不兼容但已豁免」继续装。
  2. 体检误报(真 bug,本机实测):install_hub_doctor 把 3 个已豁免的插件报成 「会被跳过」—— 而那三只当时工具全都好用。现在:体检也带豁免判定,分成 「真会被跳过(error)」与「已豁免(info,只知情)」,外加「豁免对当前 DSH 已失效」的 warn(升级后常见)。
  3. 面板里直接授权:对「装到一半被版本判据挡住」的任务给一张卡 —— 点名 包@精确版本 + 当前 DSH + 不满足的范围,二次确认(写明可能崩溃/损坏数据、只对这一个版本生效、写进 compatibility.json)→ 走宿主 setVersionExemption(...) → 回读确认 → 开新任务重试。 目标由服务端从预检结论推出,客户端不能自带包名/版本(不能用来预先放宽一个兼容的包); 五个工具都不暴露它。
  4. 降级判定对齐宿主(真 bug):版本侦测不到时,本地 semver 用严格预发布规则会把 17 个插件判成不兼容(宿主真值 3 个)。现在两条修正:evaluateCompat 接受调用方显式 传入的运行版本;本地判定改用宿主的宽容口径(includePrerelease)。修完实测与宿主完全一致: incompatible=0 / exempted=3。

R11(2026-10-22 02:00)「等确认的现场不许被按时间清掉」+ 状态里多一行「环境注意」:

  1. staging 生命周期按「任务还有没有后续动作」判(真 bug):prune() 原来只按 24 小时清 data/staging/*,于是一个「等待确认」的任务放一天多再点确认,解包现场已经没了 —— 用户看到「文件不在」而他什么也没做错。现在:非终态任务(queued / running / awaiting-confirm)的现场一律不按时间清;终态的老现场照常清。
  2. install_hub_status 多一行「环境注意」:把「版本豁免 N 条(M 条对当前 DSH 已失效: …)」与「待批准安装脚本 K 个(…)—— 下次装同一个依赖还会卡住」压成一行。这两件事 不提醒就等于没有(一个让插件安静消失、一个让下一次安装卡在同一处),但都只读、只提醒, 修/批仍然只能在面板里由人点头。
  3. 自检 372 → 378(+6:staging 真目录 + 真 mtime 验证两条 / 环境注意四态), 自洽 52 → 55(+3:三处接线)。

R11.1(2026-10-22 02:05)「跑的是不是新代码」变成自动答案(lib/codestamp.js): R9/R10 各踩一次「改了 lib/* 却没换新」,当时只能人工做行为探针(加一个动作、再打一次请求看谁回话)。 现在:模块首次被 import 时把 index.js + client.js + lib/*.js 的内容哈希记在内存里(那就是真正被加载的代码), 之后 /health 的 code 与 install_hub_status 的「运行中的代码」一行直接回答: 与磁盘一致(31 个文件 · ab12…) 或 旧(磁盘已改:lib/x.js,需要重启 DSH 才会换新)。 构建标签(源码 mtime)从此只用于「磁盘上新不新」,不再冒充「跑的是哪一版」。 自检 378 → 383(+5:freshness 判定 / 指纹覆盖面 / 两种人话 / 降级),自洽 55 → 57(+2)。

R12(2026-10-22 02:3x)「我装了哪些、哪些能升级」—— 面板第一次有"已有资产"的视角: 以前它只会装:想升级一个已装插件得自己记得名字、搜一次、再提交一次,而「哪些落后了」 从来没被回答过。现在多一张已装插件卡(折叠时只说规模,展开才逐条列):

  1. lib/inventory.js(纯本地读,30 秒缓存):把 profile 的 44 条依赖按声明形态分成四类, 它们的「能不能升级」完全不同——npm 范围才有 registry 最新版可比;link: 是本机源码目录 (改了就生效,没有"新版本")、file: 是本面板重打包的 .tgz(升级要重新提交新包)、 github: 的版本在仓库里而不在 registry 上。混为一谈就会产出真误导(把本机目录说成"落后")。 @deepseek-ai/* 一律标「宿主自带、不参与升级」——从 npm 装第二份会遮蔽宿主(dsh-tools 那个雷的同类)。
  2. lib/updates.js(只在人点「检查更新」时联网):registry / /latest 比对, 失败逐条隔离(404/超时/被墙不拖垮整张表)、30 分钟 TTL、并发封顶 4、弃用标记原样带回。 本机实测:17 个可比对的包里 7 个可升级(dsh-checkpoint-rewind 0.6.24→0.6.33 等)。
  3. 可升级行直接给「升级到 x.y.z」按钮 → 走 npm:@ 交给原流水线(预检/快照/诊断/回滚一个不少)。
  4. 搜索结果与状态工具也接上了:命中带「已装 vX」标记(不重复装同一个包); install_hub_status({updates:true}) 多「已装 / 更新 / 可升级」三行(默认不联网)。
  5. 顺手修了一个真 bug:输入 npm:包名@版本 时把 npm: 前缀当成了包名的一部分 → registry 404 → 静态扫描被跳过(安全降级),安装时又把非法 spec 交给 pnpm。现在两处都剥掉前缀。 自检 383 → 453(+70:清单判据 24 / 更新比对 20 / npm 前缀 4 / 接线 9 / codestamp re-apply 5 / 其它 8), 渲染 62 → 80(+18:折叠态 / 展开态 / 可升级按钮 / 三种兜底态),自洽 57 → 69(+12)。 渲染脚本从此自己比对条数(EXPECTED_TOTAL)—— 源码里 check( 有 103 处、实际只跑 80 条 (其余在 else/catch 兜底分支里),数源码不等于数断言。

R12.1(同一轮顺手):把「运行中的代码」那一行从会误报改成会说清。 实测撞出来的:/inventory 已经回 200(新代码在跑),可这一行还在说「旧 · 需要重启 DSH」—— 根因是「指纹只在 codestamp.js 自己被加载时算一次」+「宿主是逐文件换新的」 (codestamp.js 没改,就一直沿用 02:04 那个实例)。判据反过来指挥人做无用功,是最坏的一种错。 修法:apply() 每次(重新)apply 都记账(recordApply() → globalThis),freshness() 多回 appliedAt / appliedAfterLoad,于是三档分开说:一致 / 「宿主已在 HH:MM 重新 apply,看行为、 别盲目重启」/「改完没有任何 re-apply → 真该重启」。它仍不是万灵药:只证明入口被重新 apply 过, 不证明每个 lib/* 都换了 —— 真判据只能是行为(新路由/新字段回不回话)。

R13(2026-10-22 12:5x)「装上了 ≠ 起来了」—— 一次真实静默失效换来的三条判据。 起因:本机 10 小时里装过 dsh-imgops 两次,两条任务都是红色「安装失败」,可 pnpm 明明是成功的。 翻回执才看清:宿主是正常返回 { application: "failed", stage: "enable", error: { code: "operation-error", diagnostic: "dsh: warning: 1 entry did not activate\nimgops (dsh-imgops): Error: cannot get property \"tools\" without inject" } } —— 包装进了 profile,但激活那一步炸了:插件入口 apply(ctx) 用了 ctx.tools,却没写 export const inject = ["tools"]。 而面板当时报的是「package manager 报告安装失败」,⑨诊断还判「一切正常」。三条病一起治:

  1. lib/inject.js(新):静态扫「入口函数体里用了 ctx.,但 inject 没声明它」。判据刻意做窄 —— 只认 apply 自己的函数体(同文件里 canvas 的 ctx.fillRect 不会被算成服务),注释里的示例不算, inject: [...](客户端 bundle 的对象写法)也算声明。修法直接给一行可粘的 export const inject = ['tools']。 接在预检(装之前拦)与诊断(装之后兜)两处。

  2. 失败分诊补上「不抛异常的那种失败」:application:"failed" 以前不走 describeInstallFailure, 于是 failure = null,面板只剩一句错的通用话。现在把宿主的 error.code + diagnostic + pnpm 输出一起喂进去, 新增 kind: "activation-failed"(标题点名「装上了但没激活」+ 缺哪个服务 + 那一行修法)。

  3. 诊断第一次看得见「行勾着但没激活」:fiberPhase 是宿主行记录里的真字段(active / null),但旧代码的 plugins.find(p => p.entryId === row.id) 永远匹配不上 —— 宿主给的是 include:,于是那一整段 「插件行没有运行」从来没生效过。现在按 include: 前缀宽匹配,并新增判据:enabled && fiberPhase !== "active" → error「装上了,但没激活」,带上宿主原话与修法。

  4. already-installed 不再算失败:第二条任务就是它(同版本再装一次)。它表示「已经在依赖里、这次没改动」, 现在收成 done + 备注,并照常跑诊断(用户真正想知道的是「它起没起来」)。

  5. 端到端又撞出第二个真 bug(同一轮修掉):把 dsh-imgops 修好、版本提到 0.1.1 重新提交, 宿主仍回 already-installed —— 因为判「装过没有」看的是依赖行里的 spec 字符串,不看文件内容; 而 vendor 包名一直是 pkg-.tgz(不含版本),换了内容也是同一个 spec。于是**「重装/升级」静默无效**, 用户只会看到一条红色的「安装失败」。修法:vendor 包名带版本(pkg--.tgz), 并把同一个插件没人引用的旧包收掉(profile 还指着的绝不删,宁可少删)。

  6. 体检也升级了:install_hub_doctor 现在会逐插件扫 inject 契约,并读 listPlugins() 的 fiberPhase 报「行开着但没激活」—— 这正是「装了怎么没反应」最常见的两个答案。

  7. 固定成插件能力(不是留在脚本里):判据抽成唯一的 lib/activation.js (scanActivation / activationFindings / summarizeActivation),被安装后诊断、环境体检、 状态工具、新增的 GET /plugin-installer/activation 四处共用;install_hub_status 多一行 「激活:47 行在跑 / 共 48 行 · 1 行没激活」,面板的体检卡直接显示对应 finding。 node dev/verify-r13.mjs --port --yes 是对应的端到端验收器(中文路径必须走它)。

断言:hosttest 453 → 506(+53:inject 六种写法/边界 14、回执分诊 10、诊断行匹配与 fiberPhase 4、 版本化 vendor 名与旧包清理 4、codestamp 的「apply 是否晚于磁盘」3、体检两项 5、激活视图 10、接线 3)。

R14(2026-10-22 14:0x)补齐「Skill」那一半:面板标题从第一版起就写着「插件 / Skill 安装」, 可是它只回答过插件那一半 —— 装了哪些技能、宿主认不认、名字有没有撞车,全都没有入口。 而本机真出现过「技能装了却从不被自动调用」,根因恰恰是这些看不见的事实。

  1. 新增「已装 Skill」卡 + GET /plugin-installer/skills:磁盘扫技能根(59 个目录,每个读 SKILL.md 的 name/description),再和宿主 ctx.skills.list() 的 SkillSummary 对账,给出四类异常: 宿主没认(磁盘有、清单没有 = 装了但用不上)/ 重名(同层两个目录声明同一个 name)/ 备份残留(.bak-* 还躺在技能根里,且带着同样的 name)/ 不会自动调用(宿主 modelInvocable=false)。 摘要一行与状态工具、体检共用同一句话(lib/skills.js 是唯一口径)。
  2. 修掉一个「读到了但读错」的真 bug:parseSkillFrontmatter 只认单行标量,于是 description: > 这种折叠块把指示符 > 当成了描述 —— 面板上显示一个「>」,用途推断还拿它去和能力比对。 本机 11 个技能是这种写法。现在按 YAML 处理三种形态(> 折叠 / | 字面 / 缩进续行)、剥 BOM、剥引号。
  3. 对账的假结论被钉死:读不到宿主清单时不许把每个技能都标成「没被认」(那是最容易犯的错)。 buildSkillView 用 hostKnown 闸门分开「对账不了」与「没被认」,两种情况的摘要与结论都不同; hosttest 里各有一条断言盯着它(ctx.get("skills") 也一样:本插件没 inject skills,只写 ctx.skills 会永远读到 undefined —— 那会得到一个「本机没有技能服务」的假结论)。
  4. 体检并入技能结论:install_hub_doctor 现在也会报「N 个技能目录宿主没认」(error)、 「N 组合法性问题」(warn)、「N 个备份残留」(info),每条都带大白话与处置步骤。