diff --git a/src/main/main-runtime-support.ts b/src/main/main-runtime-support.ts index d4354d04..5e932b5d 100644 --- a/src/main/main-runtime-support.ts +++ b/src/main/main-runtime-support.ts @@ -168,6 +168,7 @@ export function createMainRuntimeSupport(state: MainRuntimeState) { bootstrapOnboarding: (input) => requireOnboardingService().bootstrapOnboarding(input), getBrowserActions: () => requireServiceGraph().getBrowserActionService(), randomFallbackCompanyId: () => requireConnectorHealthService().randomFallbackCompanyId(), + skillService: requireServiceGraph().getOrCreateSkillService(), }); } diff --git a/src/main/service-graph.ts b/src/main/service-graph.ts index aae90abd..5ab02bf6 100644 --- a/src/main/service-graph.ts +++ b/src/main/service-graph.ts @@ -11,6 +11,7 @@ import { createOrchestrationService } from "./orchestration-service"; import { createUpdaterService } from "./updater-service"; import { createWorkerService } from "./worker-service"; import { createAppLifecycleService } from "./app-lifecycle-service"; +import { createSkillService } from "./skill-service"; import type { DesktopEvent, RunRecord } from "@shared/types"; import type { AutomationEventContext } from "./automation-service"; import type { NotificationOptions } from "./notification-service"; @@ -67,8 +68,20 @@ export function createServiceGraph({ let workerService: ReturnType | null = null; let updaterService: ReturnType | null = null; let appLifecycleService: ReturnType | null = null; + let skillService: ReturnType | null = null; - const wakeAgentIfPossible = (agentId: string | null | undefined, companyId: string, trigger: string) => { + function getOrCreateSkillService() { + if (!skillService) { + skillService = createSkillService({ + db, + profileDir: db.profileDir, + logger, + }); + } + return skillService; + } + + const wakeAgentIfPossible =(agentId: string | null | undefined, companyId: string, trigger: string) => { if (!orchestrationService) { throw new Error("Orchestration service is not initialized."); } @@ -236,6 +249,7 @@ export function createServiceGraph({ orchestrationService = null; updaterService = null; appLifecycleService = null; + skillService = null; db.close(); // Force exit after grace period — covers all platforms (macOS Dock, Windows tray, Linux) // Worker child processes (claude/codex CLI) may keep the event loop alive indefinitely @@ -255,5 +269,6 @@ export function createServiceGraph({ getOnboardingService: () => onboardingService, getUpdaterService: () => updaterService, getAppLifecycleService: () => appLifecycleService, + getOrCreateSkillService, }; } diff --git a/src/preload/index.ts b/src/preload/index.ts index d55e5e99..3f6d2610 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -76,6 +76,20 @@ const api: DesktopApi = { deleteWorkflow: (input) => ipcRenderer.invoke(IPC_CHANNELS.deleteWorkflow, input), runWorkflow: (input) => ipcRenderer.invoke(IPC_CHANNELS.runWorkflow, input), autoAssignTask: (input) => ipcRenderer.invoke(IPC_CHANNELS.autoAssignTask, input), + importSkill: (companyId, source) => + ipcRenderer.invoke(IPC_CHANNELS.importSkill, { companyId, source }), + listSkills: (companyId) => + ipcRenderer.invoke(IPC_CHANNELS.listSkills, { companyId }), + getSkillDetail: (companyId, skillId) => + ipcRenderer.invoke(IPC_CHANNELS.getSkillDetail, { companyId, skillId }), + deleteSkill: (companyId, skillId) => + ipcRenderer.invoke(IPC_CHANNELS.deleteSkill, { companyId, skillId }), + assignSkillToAgent: (companyId, agentId, skillId) => + ipcRenderer.invoke(IPC_CHANNELS.assignSkillToAgent, { companyId, agentId, skillId }), + removeSkillFromAgent: (companyId, agentId, skillId) => + ipcRenderer.invoke(IPC_CHANNELS.removeSkillFromAgent, { companyId, agentId, skillId }), + checkSkillUpdate: (companyId, skillId) => + ipcRenderer.invoke(IPC_CHANNELS.checkSkillUpdate, { companyId, skillId }), recoveryRetry: (taskId: string, agentId: string, companyId: string) => ipcRenderer.invoke("recovery:retry", { taskId, agentId, companyId }), recoveryDismiss: (taskId: string) =>