시작 단계
Elftia의 메인 프로세스 시작 흐름은 13개 단계로 나뉘며, 각 단계에는 명확하게 정의된 책임과 의존성이 있습니다. 모든 단계 코드는 packages/desktop/app/main/index.ts의 createServices() 함수에 있습니다.
시작 단계 개요
flowchart TB
P0[Phase 0: Protocol Registration + Logger]
P1[Phase 1: Core Services]
P2[Phase 2: Feature Services]
P25[Phase 2.5: Package Loading]
P26[Phase 2.6: Package Sync]
P27[Phase 2.7: Package Management Service]
PS1[Phase S1: Magi Dispatch]
PS2[Phase S2: MagiService]
PS25[Phase S2.5: CronService]
PC1[Phase C1: Channel Plugins]
PS3[Phase S3: ChannelMagiBridge]
P3[Phase 3: IPC Route Registration]
PT[Phase T1-T3: Todo/Task/Notes]
PW1[Phase W1: Window Creation]
PW2[Phase W2: WebSocket/Protocol]
P0 --> P1 --> P2 --> P25 --> P26 --> P27
P27 --> PS1 --> PS2 --> PS25
PS25 --> PC1 --> PS3
PS3 --> P3 --> PT --> PW1 --> PW2
Phase 0: Protocol Registration + Logger
app.whenReady() 전에 실행됩니다. 사용자 지정 프로토콜과 핵심 로깅 서비스를 등록합니다.
flowchart LR
A[Register elftia:// protocol] --> B[Register privileged schemes]
B --> C[wallpaper://]
B --> D[media://]
B --> E[resource://]
| 단계 | 작업 | 설명 |
|---|---|---|
| 1 | app.setAsDefaultProtocolClient('elftia') | Deep Link 프로토콜 등록 |
| 2 | protocol.registerSchemesAsPrivileged() | wallpaper/media/resource 권한 프로토콜 등록 |
| 3 | new LoggerService() | 로깅 서비스 생성(Winston + daily-rotate) |
| 4 | setIpcLogger(logger) | IPC 계층 logger 설정 |
| 5 | new ErrorHandler(logger) | 전역 오류 핸들러 생성 |
| 6 | setIpcErrorHandler(errorHandler) | IPC 계층 오류 핸들러 설정 |
| 7 | new AutoUpdateService(logger) | 자동 업데이트 서비스 생성 |
| 8 | new DownloadService(logger) | 다운로드 서비스 생성 |
| 9 | new WebUpdateService(logger, downloadManager) | 웹 업데이트 서비스 생성 |
주요 파일: packages/desktop/app/main/services/infra/logger/LoggerService.ts, packages/desktop/app/main/services/infra/error/ErrorHandler.ts
Phase 1: Core Services
애플리케이션 실행에 필요한 기반 서비스를 생성합니다. 이 서비스들은 외부 의존성이 없습니다.
flowchart LR
RT[RuntimeService] --> AP[AppPaths]
AP --> DB[DbClient + ready]
DB --> SS[SettingsService]
SS --> CS[ConfigStore]
CS --> Migrate[Config Migration]
Migrate --> Auth[Auth Service Chain]
Auth --> WC[WindowControlsService]
| 단계 | 서비스 | 의존성 | 설명 |
|---|---|---|---|
| 1 | RuntimeService | None | 런타임 정보(버전, 플랫폼, 채널) |
| 2 | AppPaths | None | 경로 해석 |
| 3 | DbClient | AppPaths | Database Worker 생성 |
| 4 | await db.ready() | DbClient | Worker 초기화가 완료될 때까지 차단 대기 |
| 5 | initDatabase() | db.ready | Drizzle ORM + WAL + migrate |
| 6 | SettingsService | AppPaths | 애플리케이션 설정 |
| 7 | ConfigStore | AppPaths | electron-store config 파일 |
| 8 | migrateSettingsToConfigStore() | Settings, ConfigStore | SQLite -> config.json 마이그레이션 |
| 9 | settings.setConfigStore(configStore) | - | Delegate 패턴 연결 |
| 10 | configStore.startWatching() | - | fs.watch config 핫 리로드 |
| 11 | AccountService | AppPaths | 사용자 자격 증명 |
| 12 | AuthTokenStore | AccountService | 토큰 저장소 |
| 13 | AuthSessionService | Runtime, AuthStore, AuthApi | 인증 세션 |
| 14 | AppPreferencesService | ConfigStore | 애플리케이션 환경설정 |
| 15 | WindowControlsService | Settings | 창 컨트롤 |
핵심 포인트: await db.ready()는 SQLite Worker가 스키마 생성을 완료한 뒤에만 진행되도록 보장하는 차단 지점입니다. 이를 통해 첫 시작 시 두 연결이 동시에 쓰기를 수행할 때 발생하는 SQLITE_BUSY 오류를 방지합니다.
Phase 2: Feature Services
가벼운 기능 서비스를 생성합니다. 무거운 초기화는 사용 시점까지 지연됩니다.
| 서비스 | 의존성 | 설명 |
|---|---|---|
ConfigManager | Logger, AppPaths | Config 파일 관리 |
SecurityService | Logger | AES-256-GCM 암호화 |
PerformanceMonitor | Logger | 성능 모니터링 |
CacheService | Logger | 범용 캐시 |
DatabaseOptimizer | Logger, DbClient | SQLite 최적화 |
NotificationService | Logger | 데스크톱 알림 |
PluginManager | Logger | 플러그인 생명주기 |
ProjectService | DbClient, AccountService | 프로젝트 관리 |
DiagnosticsService | AppPaths, ProjectService | 진단 서비스 |
ChatService | Logger | 채팅 서비스 |
McpService | Logger | MCP server 관리 |
LLMConfigService | AppPaths, DbClient | LLM 설정 |
CompletionService | AppPaths, LLMConfigService | LLM completion |
ApiKeyPoolService | DB, CompletionService | 다중 키 라운드로빈 |
ElfiService | ConfigStore, LLMConfig, Completion | Elfi assistant |
MediaConfigService | AppPaths, DbClient | 미디어 설정 |
ImageGenerationService | MediaConfig | 이미지 생성 |
ThemeService | Settings, AuthSession | 테마 관리 |
AgentService | DbClient, Settings, Logger | Agent 서비스 |
TransformerService | - | 요청 변환 |
WebSearchService | - | 웹 검색 |
EngineDispatcher + engine registration | 각 engine의 의존성 | Engine dispatch |
Engine 등록 순서
{/* Engine registration */}
engineDispatcher.registerEngine(new ApiEngine(...));
engineDispatcher.registerEngine(new ChatEngine(...));
engineDispatcher.registerEngine(new ClaudeSdkEngine(...));
engineDispatcher.registerEngine(new CliRunnerEngine(...));
engineDispatcher.registerEngine(new TinyElfEngine(...));
engineDispatcher.registerEngine(new STChatEngine(...));
Phase 2.5: Package Loading
Agent 패키지, Persona 패키지, Script 플러그인을 디스크에서 메모리로 로드합니다.
flowchart TB
subgraph "Phase 2.5a: Agent Packages"
AL[AgentPackageLoader] --> BP[Load builtin packages]
BP --> UP[Load user packages]
UP --> AR[AgentPackageRegistry]
end
subgraph "Phase 2.5b: Persona Packages"
PL[PersonaPackageLoader] --> BPP[Load builtin packages]
BPP --> UPP[Load user packages]
UPP --> PR[PersonaPackageRegistry]
end
subgraph "Phase 2.5c: Script Plugins"
SL[ScriptPluginLoader] --> BSP[Load builtin plugins]
BSP --> USP[Load user plugins]
USP --> SR[ScriptPluginRegistry]
end
로드 경로(이중 디렉터리 패턴):
| 패키지 유형 | 내장 경로(패키징됨) | 내장 경로(dev) | 사용자 경로 |
|---|---|---|---|
| Agent | resources/agent-packages/ | ../../agent-packages/ | {userData}/agent-packages/ |
| Persona | resources/persona-packages/ | ../../persona-packages/ | {userData}/persona-packages/ |
| Script | resources/script-plugins/ | ../../script-plugins/ | {userData}/script-plugins/ |
Phase 2.6-2.7: Package Sync and Management
| Phase | 작업 | 설명 |
|---|---|---|
| 2.6 | PackageSyncService.syncAll() | 인메모리 패키지 정의를 데이터베이스에 동기화합니다(upsert FK anchors + persona data) |
| 2.7 | new AgentPackageService(...) | install/uninstall을 지원하는 패키지 관리 서비스를 생성합니다 |
Phase S1: Magi Dispatch
Agent 오케스트레이션 및 dispatch와 관련된 서비스를 생성합니다.
flowchart TB
AGM[AgentManagementService] --> EB[SessionEventBus]
EB --> AD[AgentDiscovery]
AD --> AO[AgentOrchestrator]
AO --> MR[MessageRouter]
MR --> SDI[SessionDispatcherImpl]
AO -.->|setEngineDispatcher| ED[EngineDispatcher]
AO -.->|setDbProvider| DB[DbClient]
SDI -.->|injected into| TE[TinyElfEngine]
| 단계 | 서비스 | 설명 |
|---|---|---|
| 1 | AgentManagementService | Agent CRUD + 패키지 레지스트리 쿼리 |
| 2 | InternalSessionEventBus | 세션 이벤트 게시/구독 |
| 3 | AgentDiscovery | 스케줄 가능한 Agent 목록(DB + 패키지 레지스트리에서 발견) |
| 4 | AgentOrchestrator | Agent dispatch 및 오케스트레이션, API dispatch 핸들러 |
| 5 | MessageRouter | 이중 모드 메시지 라우팅(직접 / Orchestrator 경유) |
| 6 | SessionDispatcherImpl | 세션 간 dispatch 구현 |
| 7 | TinyElfEngine에 주입 | tinyElfEngine.setSessionDispatcher(sessionDispatcher) |
| 8 | SubagentRegistry.setDb(db) | Subagent 레지스트리 write-through 영속화 |
역사적 참고:
DispatchServerHTTP dispatch server는cleanup-legacy-mcp(2026-05-19)에서 제거되었습니다. 이제dispatch도구는 통합 레지스트리를 통해MagiDispatchProvider+MagiDispatchHandlers로 프로세스 내부에서 조립됩니다. 핸들러는 HTTP를 거치지 않고AgentOrchestrator/AgentDiscovery를 직접 호출합니다. CLI subprocess는 중앙services/capabilities/tools/mcp-builtin/BuiltinMcpHttpServer.ts를 통해 이에 접근합니다.
Phase S2: MagiService
핵심 메시지 처리 서비스를 생성하고 모든 의존성을 주입합니다.
{/* MagiService dependency injection chain */}
const magiService = new MagiService(
magiWorkspace, magiSession, magiOptionsBuilder,
magiOrchestrator, magiMessageRouter, magiEventBus,
llmConfig, logger, configStore,
);
// Deferred injection (avoids circular dependencies)
magiService.setAgentService(agent);
magiService.setCompletionService(completion);
magiService.setAsrService(new AsrService(asrConfig));
magiService.setEngineDispatcher(engineDispatcher);
magiService.setDbProvider(db);
magiService.setMcpListProvider(() => mcp.list());
magiService.setDeveloperModeProvider(() => ...);
MCP 핫 리로드: MCP server 목록이 변경되면 Magi workspace에 자동으로 동기화되고 활성 세션이 핫 리로드됩니다.
mcp.on('servers:changed', () => {
magiService.syncMcpServersToWorkspace();
magiService.notifyMcpChanged();
});
Phase S2.5: CronService
Cron 예약 작업 서비스를 생성합니다.
| 주입 | 목적 |
|---|---|
setNotificationService(notification) | 데스크톱 알림 |
setNotificationChecker(...) | 알림 환경설정 확인 |
setAgentExecutor(...) | MagiService를 통해 예약 작업 실행 |
setStatusBroadcaster(...) | 작업 상태를 frontend에 브로드캐스트 |
Phase C1: Channel Plugins
Channel 플러그인을 발견, 로드, 등록합니다.
flowchart TB
CL[ChannelPluginLoader] --> Discover[discover plugins]
Discover --> Load[load each plugin]
Load --> CR[ChannelPluginRegistry.registerPlugin]
CR --> CM[ChannelMarketplaceService<br/>async fetch remote manifest]
subgraph "Security Service Wiring"
UPS[UserPermissionService] --> CMR[ChannelMessageRouter]
CPG[ChannelPermissionGate] --> CMR
AL[AuditLogger] --> TE[TinyElfEngine]
RL[RateLimiter] --> CMR
IS[InputSanitizer] --> CMR
PG[PromptGuardian] --> CMR
end
CR --> CMR
로드 경로(이중 디렉터리 패턴):
| 소스 | 경로(패키징됨) | 경로(dev) |
|---|---|---|
| Bundled (read-only) | resources/bundled-channel-plugins/ | elftia-channels/dist-plugins/ |
| Marketplace (writable) | {userData}/channel-plugins/ | same |
보안 서비스 연결 순서:
UserPermissionService-> ChannelMessageRouter (사용자 권한 확인)ChannelPermissionGate-> ChannelMessageRouter + TinyElfEngine (권한 확인)AuditLogger-> TinyElfEngine (감사 로깅)RateLimiter-> ChannelMessageRouter (속도 제한)InputSanitizer-> ChannelMessageRouter (입력 정리)PromptGuardian-> ChannelMessageRouter (프롬프트 인젝션 감지)
Phase S3: ChannelMagiBridge
Channel 이벤트를 MagiService에 연결합니다.
const channelMagiBridge = new ChannelMagiBridge(
channelRegistry,
channelMessageRouter,
magiService,
logger,
);
역사적 참고:
ChannelServerHTTP server는cleanup-legacy-mcp(2026-05-19)에서 제거되었습니다. 이제channel_send/channel_list및 기타 도구는 통합 레지스트리를 통해MagiChannelProvider+MagiChannelHandlers로 프로세스 내부에서 조립됩니다. 핸들러는ChannelPluginRegistry/ChannelMessageRouter를 직접 호출합니다.
Phase 3: IPC Route Registration
모든 IPC 핸들러는 창 생성 전에 등록됩니다.
const router = new Router(/* 60+ service dependencies */);
const { skillHubDownloadService } = router.register();
registerAllRouters()는 내부적으로 68개 이상의 Router 인스턴스를 생성하고 등록합니다.
| Router 클래스 | 채널 접두사 |
|---|---|
AuthRouter | auth:* |
ProjectRouter | projects:* |
SessionRouter | sessions:* |
CompletionRouter | completion:* |
LLMRouter | llmProviders:*, llmModels:* |
MediaRouter | media:* |
MagiRouter | magi:* |
ChannelPluginRouter | channels:* |
SecurityRouter | security:* |
TodoRouter | todo:* |
TaskRouter | tasks:* |
NoteRouter | notes:* |
SubagentRouter | subagents:* |
ElfiRouter | elfi:* |
EnvironmentRouter | env:* |
| ... and 50+ other routers | ... |
Phase T1-T3: Todo/Task/Notes
flowchart LR
T1[Phase T1: TodoService] --> T1a[initialize — load TODO.md]
T2[Phase T2: TaskService] --> T2a[initialize — scan tasks]
T3[Phase T3: NoteFileService + NoteWatcher] --> T3a[ensureVaultDir]
T3a --> T3b[scanAllNotes + reindex]
T3b --> T3c[DB notes migration check]
| Phase | 서비스 | 작업 |
|---|---|---|
| T1 | TodoService | Markdown 할 일 시스템 초기화 |
| T2 | TaskService | 파일시스템 + SQLite 작업 관리 초기화 |
| T3 | NoteFileService | vault 디렉터리가 존재하는지 확인 |
| T3 | NoteWatcher | 파일시스템 watcher |
| T3 | Notes migration | DB에서 .md 파일로 1회 마이그레이션 |
| T3 | Startup reindex | 모든 노트를 스캔하고 DB 인덱스 업데이트 |
Phase W1: Window Creation
모든 서비스와 IPC route가 등록된 후 BrowserWindow를 생성합니다.
mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
preload: preloadPath,
},
});
Phase W2: WebSocket/Protocol Handling
창 생성 후 초기화:
| 단계 | 작업 |
|---|---|
| 1 | wallpaper:// 프로토콜 핸들러 등록 |
| 2 | media:// 프로토콜 핸들러 등록 |
| 3 | resource:// 프로토콜 핸들러 등록 |
| 4 | renderer URL 로드(dev server 또는 file://) |
| 5 | 대기 중인 Deep Link 처리 |
| 6 | Channel 인스턴스 복원(autoConnect) |
| 7 | Content Security Policy 설정 |
| 8 | Navigation 가로채기(isAllowedNavigation) |
시작 타이밍
createServices()는 주요 단계의 타이밍을 기록합니다.
[INFO] Core services initialized { duration: ~50ms }
[INFO] Feature services created { duration: ~200ms }
[INFO] Agent packages loaded { builtin: N, user: M }
[INFO] Persona packages loaded { builtin: N, user: M }
[INFO] Script plugins loaded { builtin: N, user: M }
[INFO] Magi dispatch services created (Phase S1)
[INFO] MagiService created (Phase S2)
[INFO] ChannelMagiBridge created (Phase S3)
[INFO] IPC handlers registered { duration: ~500ms }
관련 파일
| 파일 | 설명 |
|---|---|
packages/desktop/app/main/index.ts | 메인 프로세스 진입점, createServices() 함수 |
packages/desktop/app/main/services/routers/index.ts | registerAllRouters() route 등록 |
packages/desktop/app/main/db/index.ts | initDatabase() 데이터베이스 초기화 |
packages/desktop/app/main/workers/DbClient.ts | Database Worker 클라이언트 |
packages/desktop/app/main/services/infra/logger/LoggerService.ts | 로깅 서비스 |
packages/desktop/app/main/services/infra/paths/paths.ts | AppPaths 경로 관리 |
packages/desktop/app/main/services/platform/config/store/ConfigStore.ts | Config 저장소 + fs.watch |
packages/desktop/app/main/services/agent-core/agent/AgentPackageLoader.ts | Agent 패키지 로더 |
packages/desktop/app/main/services/agent-core/agent/PersonaPackageLoader.ts | Persona 패키지 로더 |
packages/desktop/app/main/services/capabilities/tools/script-plugin/ScriptPluginLoader.ts | Script 플러그인 로더 |
packages/desktop/app/main/services/capabilities/integrations/channel/ChannelPluginLoader.ts | Channel 플러그인 로더 |
packages/desktop/app/main/services/agent-core/magi/MagiService.ts | Magi 핵심 서비스 |
packages/desktop/app/main/services/agent-core/magi/AgentOrchestrator.ts | Agent dispatch 오케스트레이터 |
packages/desktop/app/main/services/platform/cron/CronService.ts | 예약 작업 서비스 |
packages/desktop/app/main/services/content/workspace/todo/TodoService.ts | 할 일 서비스 |
packages/desktop/app/main/services/content/workspace/tasks/TaskService.ts | 작업 관리 서비스 |
packages/desktop/app/main/services/content/workspace/notes/NoteFileService.ts | 노트 파일 서비스 |