본문으로 건너뛰기

频道插件打包(official / steam)

频道插件(Discord、Feishu、Telegram、Slack、WhatsApp……)以预构建产物的形式随安装包分发。本文说明它们从源码到运行时的完整路径、不同构建渠道(official / steam)的打包差异,以及为什么 steam 渠道曾经漏打、现在如何用审计兜底。

两个发现目录

运行时由 ChannelPluginLoader两个目录发现频道插件,pluginsDir 优先级高于 bundledDir(同 type 时覆盖):

目录路径读写来源
用户安装(pluginsDir<userData>/channel-plugins/读写Marketplace 下载 / 本地安装
内置(bundledDir<resources>/bundled-channel-plugins/只读随安装包分发

相关代码:packages/desktop/app/main/services/capabilities/integrations/channel/ChannelPluginLoader.tspackages/desktop/app/main/bootstrap/capabilities-channel.ts

bundledDir 的解析见 bootstrap/index.ts

const channelBundledDir = isDev
? path.join(__dirname, '..', '..', '..', 'elftia-channels', 'dist-plugins') // 开发态:workspace 源目录
: path.join(process.resourcesPath, 'bundled-channel-plugins'); // 打包态:resources 下的拷贝

ChannelPluginLoader 构造时对 bundledDir 做存在性判断:

this.bundledDir = bundledDir && existsSync(bundledDir) ? bundledDir : null;

关键含义:如果打包态 <resources>/bundled-channel-plugins/ 不存在,bundledDir 会被置为 nulldiscover() 就只剩 <userData>/channel-plugins/ 一个目录。对全新安装的用户,该目录为空 → 发现 0 个频道插件 → 频道页没有任何可用插件

从源码到安装包

频道插件源码构建产物落在 workspace 目录:

packages/elftia-channels/dist-plugins/
├── discord/
├── feishu/
├── telegram/
├── slack/
├── whatsapp/
└── …(共 ~26 个)

packages/elftia-channels/dist-plugins预构建产物目录,不在 build:official / build:steam 的内联构建链里——打包前需已存在。

打包时由 electron-builder 的 extraResources 把它拷进 resources/bundled-channel-plugins两个构建渠道都必须声明这一条

# electron/electron-builder.official.yml 与 electron/electron-builder.steam.yml 都需要:
extraResources:
- from: packages/elftia-channels/dist-plugins
to: bundled-channel-plugins
filter:
- "**/*"

official 与 steam 的打包差异

officialsteam
配置文件electron/electron-builder.official.ymlelectron/electron-builder.steam.yml
renderer-extension/byo-providers打入(BYO 是官网版核心承诺)排除(Steam 合规切割,filter 显式负向匹配)
bundled-channel-plugins打入必须同样打入

频道插件与 BYO 合规切割无关:频道插件使用用户自己的 bot token(Discord bot token、Telegram bot token 等),不属于 Steam 拒审的「BYO LLM provider key / 站外付费」范畴。Steam 渠道唯一的合规排除是 renderer-extension/byo-providers,频道插件不在排除之列。

曾经的故障:steam.yml 漏配

electron-builder.steam.ymlextraResources 一度整条缺失 bundled-channel-plugins(全 git 历史里从未出现过)。后果:

  1. Steam 包里没有 resources/bundled-channel-plugins/ 目录;
  2. 运行时 channelBundledDir 指向不存在的路径 → ChannelPluginLoader.bundledDir = null
  3. discover() 只扫空的用户目录 → 发现 0 个频道插件;
  4. 用户现象:Steam 版无法使用频道插件,官网版正常

这条排除不是有意的合规切割(所有有意排除都带注释 + filter 负向匹配),而是 steam.yml 从 official.yml 分叉时遗漏的一条 extraResources

审计兜底:verify:steam-package

scripts/verify-steam-packaging.mtsnpm run verify:steam-package,已接入 build:steam 链尾)现在校验四条不变量

不变量含义
byo-providers ABSENTSteam 包不含 renderer-extension/byo-providers(合规)
design-studio PRESENTSteam 包 renderer-extension/design-studio
channel === 'steam'渠道元数据为 steam
bundled-channel-plugins PRESENTSteam 包至少打入 1 个频道插件 ← 本次新增

退出码契约:0 全过;2 至少一条违反;1 输入不可用。

审计有两种模式(按优先级):

  • MODE 1(已打包,权威):若存在 release/steam/<platform>-unpacked/…/resources/plugins/renderer-extension/,直接审计真实产物;频道插件数 = 同级 resources/bundled-channel-plugins/ 的子目录数。
  • MODE 2(暂存干跑,回退):解析 electron-builder.steam.yml 里映射到 bundled-channel-pluginsextraResources 条目,统计其 from: 源目录的子目录数;没有这条 → 计为 0 → 不变量失败(正是这次要拦的漏配)。

历史教训:之前的审计只看 renderer-extension,频道插件整体不在其视野里——所以包里 0 个频道插件时审计依然全绿。新增的 bundled-channel-plugins PRESENT 不变量堵上了这个盲区。

修改 / 验证清单

新增或调整频道插件打包时:

  • electron-builder.steam.ymlofficial.yml 声明了 packages/elftia-channels/dist-plugins → bundled-channel-plugins
  • 打包前 packages/elftia-channels/dist-plugins/ 已构建且非空;
  • 重新打包:npm run build:steam:win(注意旧的 release/steam/win-unpacked/ 是上次产物,审计 MODE 1 会以它为准——务必重新打包后再审计);
  • 产物核对:release/steam/win-unpacked/resources/bundled-channel-plugins/ 下有全部插件目录;
  • 审计通过:npm run verify:steam-package 退出码 0[PASS] bundled-channel-plugins PRESENT
  • 运行时核对:主进程日志 Channel plugin directories … bundledExists: trueChannel plugin discover: found N plugin(s)(N > 0)。