import type { AgentApiServer } from "./api-server"; import type { AppDatabase } from "./database"; import { BrowserManager } from "./browser-manager"; import { createAutomationService } from "./automation-service"; import { createBrowserActionService } from "./browser-action-service"; import { createConnectorHealthService } from "./connector-health-service"; import { createDesktopShellService } from "./desktop-shell-service"; import { createOnboardingService } from "./onboarding-service"; import { createOrchestrationService } from "./orchestration-service"; import { createUpdaterService } from "./updater-service"; import { createWorkerService } from "./worker-service"; import { createAppLifecycleService } from "./app-lifecycle-service"; import type { DesktopEvent, RunRecord } from "@shared/types"; import type { AutomationEventContext } from "./automation-service"; interface ServiceGraphDependencies { db: AppDatabase; apiServerRef: { current: AgentApiServer | null }; browserManagerRef: { current: BrowserManager | null }; isQuittingRef: { current: boolean }; preloadPath: string; rendererDist: string; iconPath: string; isDev: boolean; devServerUrl?: string; windowStatePath: string; workerPath: string; logger: { info: (...args: unknown[]) => void; warn: (...args: unknown[]) => void; error: (...args: unknown[]) => void; }; emitEvent: (event: DesktopEvent) => void; publishDomainChanged: () => void; notify: (title: string, body: string) => void; createRunToken: (agentId: string, companyId: string, runId: string) => string; seedDefaultAutomationRules: (companyId: string) => void; } export function createServiceGraph({ db, apiServerRef, browserManagerRef, isQuittingRef, preloadPath, rendererDist, iconPath, isDev, devServerUrl, windowStatePath, workerPath, logger, emitEvent, publishDomainChanged, notify, createRunToken, seedDefaultAutomationRules, }: ServiceGraphDependencies) { let automationService: ReturnType | null = null; let browserActionService: ReturnType | null = null; let connectorHealthService: ReturnType | null = null; let onboardingService: ReturnType | null = null; let desktopShellService: ReturnType | null = null; let orchestrationService: ReturnType | null = null; let workerService: ReturnType | null = null; let updaterService: ReturnType | null = null; let appLifecycleService: ReturnType | null = null; const wakeAgentIfPossible = (agentId: string | null | undefined, companyId: string, trigger: string) => { if (!orchestrationService) { throw new Error("Orchestration service is not initialized."); } return orchestrationService.wakeAgentIfPossible(agentId, companyId, trigger); }; const dispatchBrowserAction = async (actionId: string, companyId: string) => { if (!browserActionService) { throw new Error("Browser action service is not initialized"); } return browserActionService.dispatchBrowserAction(actionId, companyId); }; const dispatchAutomation = (trigger: string, ctx: AutomationEventContext) => { if (!automationService) { throw new Error("Automation service is not initialized."); } automationService.dispatchAutomation(trigger, ctx); }; const processRunCompletion = (run: RunRecord, event: Parameters["processRunCompletion"]>[1]) => { if (!automationService) { throw new Error("Automation service is not initialized."); } automationService.processRunCompletion(run, event); }; const queueTaskRun = (taskId: string, trigger: string, actor: string) => { if (!orchestrationService) { throw new Error("Orchestration service is not initialized."); } return orchestrationService.queueTaskRun(taskId, trigger, actor); }; const dispatchRunToWorker = (params: Parameters["dispatchRunToWorker"]>[0]) => { if (!orchestrationService) { throw new Error("Orchestration service is not initialized."); } return orchestrationService.dispatchRunToWorker(params); }; function init() { desktopShellService = createDesktopShellService({ preloadPath, rendererDist, iconPath, isDev, devServerUrl, windowStatePath, shouldQuit: () => isQuittingRef.current, onRequestQuit: () => { isQuittingRef.current = true; }, onCheckForUpdates: async () => { if (!updaterService) throw new Error("Updater service is not initialized."); await updaterService.checkForUpdates(); }, onBackupProfile: async () => { const target = await import("electron").then(({ dialog }) => dialog.showOpenDialog({ properties: ["openDirectory", "createDirectory"], })); if (target.canceled || target.filePaths.length === 0) return; await db.exportBackup(`${target.filePaths[0]!}/agentcompany-backup-${Date.now()}`); }, onShortcut: (action) => emitEvent({ type: "shortcut", action }), notify, }); desktopShellService.initialize(); connectorHealthService = createConnectorHealthService({ db, logger, publishDomainChanged, }); workerService = createWorkerService({ db, logger, workerPath, isQuitting: () => isQuittingRef.current, publishDomainChanged, emitEvent, wakeAgentIfPossible, dispatchRunToWorker, processRunCompletion: (run, event) => processRunCompletion(run, event), notify, }); orchestrationService = createOrchestrationService({ db, logger, publishDomainChanged, notify, createRunToken, getApiPort: () => apiServerRef.current?.getPort() ?? 0, dispatchAutomation, hasWorkerReady: () => Boolean(workerService?.getWorker()), enqueueWorkerRun: (payload) => { workerService?.getWorker()?.postMessage({ type: "enqueue-run", payload }); }, }); workerService.createWorker(); automationService = createAutomationService({ db, logger, emitEvent, publishDomainChanged, notify, wakeAgentIfPossible, dispatchBrowserAction, }); onboardingService = createOnboardingService({ db, seedDefaultAutomationRules, queueTaskRun, publishDomainChanged, }); browserActionService = createBrowserActionService({ db, browserManagerRef, publishDomainChanged, }); browserActionService.resetBrowserManager(); updaterService = createUpdaterService({ db, logger, notify, }); updaterService.attachListeners(); appLifecycleService = createAppLifecycleService({ getMainWindow: () => desktopShellService?.getMainWindow() ?? null, ensureWindow: async () => desktopShellService?.ensureWindow() ?? null, shouldQuit: () => isQuittingRef.current, onBeforeQuit: () => { isQuittingRef.current = true; orchestrationService?.stopHeartbeatScheduler(); apiServerRef.current?.stop(); apiServerRef.current = null; browserManagerRef.current?.shutdown(); browserManagerRef.current = null; desktopShellService?.destroy(); desktopShellService = null; workerService?.destroy(); workerService = null; browserActionService = null; automationService = null; connectorHealthService = null; onboardingService = null; orchestrationService = null; updaterService = null; appLifecycleService = null; db.close(); }, }); } return { init, getDesktopShellService: () => desktopShellService, getConnectorHealthService: () => connectorHealthService, getWorkerService: () => workerService, getBrowserActionService: () => browserActionService, getOrchestrationService: () => orchestrationService, getAutomationService: () => automationService, getOnboardingService: () => onboardingService, getUpdaterService: () => updaterService, getAppLifecycleService: () => appLifecycleService, }; }