본문으로 건너뛰기

채널 플러그인 패키징 (official / steam)

채널 플러그인 (Discord, Feishu, Telegram, Slack, WhatsApp 등)은 미리 빌드된 산출물로 설치 파일과 함께 배포됩니다. 이 문서에서는 소스 코드에서 런타임까지의 전체 경로, 빌드 채널(official / steam) 간 패키징 차이, 그리고 steam 채널에서 한때 플러그인이 누락되었던 이유와 감사로 이를 방지하는 방법을 설명합니다.

두 가지 검색 디렉토리

런타임에 ChannelPluginLoader두 디렉토리에서 채널 플러그인을 검색하며, pluginsDirbundledDir보다 우선순위가 높습니다 (동일 type인 경우 덮어씀):

디렉토리경로읽기/쓰기소스
사용자 설치 (pluginsDir)<userData>/channel-plugins/읽기/쓰기Marketplace 다운로드 / 로컬 설치
내장 (bundledDir)<resources>/bundled-channel-plugins/읽기 전용설치 파일과 함께 배포

관련 코드: packages/desktop/app/main/services/capabilities/integrations/channel/ChannelPluginLoader.ts, packages/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/가 존재하지 않으면 bundledDirnull로 설정되고, discover()<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는 official의 핵심 약속)제외 (Steam 규정 준수 분리, 명시적 음수 filter 매칭)
bundled-channel-plugins포함반드시 포함해야 함

채널 플러그인은 BYO 규정 준수 분리와 무관합니다: 채널 플러그인은 사용자의 자체 봇 토큰 (Discord 봇 토큰, Telegram 봇 토큰 등)을 사용하므로, Steam이 거부하는 "BYO LLM 제공업체 키 / 외부 결제" 범주에 해당하지 않습니다. 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 버전에서 채널 플러그인 사용 불가, official 버전은 정상 작동.

이 누락은 의도적인 규정 준수 분리가 아니었으며 (모든 의도적 제외에는 주석 + 음수 filter 매칭이 있음), steam.ymlofficial.yml에서 분기될 때 누락된 extraResources 항목이었습니다.

감사 대비책: verify:steam-package

scripts/verify-steam-packaging.mts (npm 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-plugins에 매핑된 extraResources 항목을 파싱하고 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) 표시.