import { contextBridge, ipcRenderer, webUtils } from 'electron' import { type ApprovalDecision, type AgentEvent, type AgentQuestionAnswer, type AgentRequest, type AgentRuntimeDetection, type AgentRuntimeStatus, type AppInfo, type BrowserLiveState, type ContextAttachment, type ContextFileSelectionProgress, type DesktopApi, type KnowledgeLibrary, type KnowledgeSearchReference, type KnowledgeSnapshot, type PastedImageInput, type RuntimeSettings, type RuntimeSettingsInput, type RuntimeConfigActionInput, type RuntimeFileSelectionKind, type WindowCaptureOption } from '../shared/contracts' import { ipcChannels } from '../shared/ipc-channels' import type { BrowserProfileCreateInput, BrowserProfileRenameInput, CapabilityDiagnosticReport, CapabilitySnapshot, ComputerCapabilityId, McpServerTestResult, WebSearchTestResult } from '../shared/capability-contracts' import type { AssistantProject, AssistantArtifact, AssistantMemory, AssistantSchedule, AssistantHeartbeatConfig, AssistantHeartbeatEntry, AssistantHeartbeatRun, AssistantExpert, AssistantTask, TokenUsageSummary, ConversationSnapshot, WorkspaceChanges, WorkspaceDirectoryListing, WorkspaceFilePreview, ProjectCreateInput, MemoryCreateInput, ScheduleCreateInput, HeartbeatCreateInput, HeartbeatUpdateInput, ExpertCreateInput, ExpertUpdateInput } from '../shared/assistant-contracts' import type { ChannelConnectionTestResult, ChannelSettingsApply, ChannelSettingsSnapshot, CredentialChannel, DingTalkChannelSettingsInput, WeComChannelSettingsInput } from '../shared/channel-settings-contracts' import type { ApplicationSettings, ApplicationSettingsUpdate, VersionCheckResult } from '../shared/application-settings-contracts' import type { ReleaseNotesSnapshot } from '../shared/release-notes-contracts' import type { SpeechModelSnapshot, SpeechTranscriptionInput, SpeechTranscriptionResult } from '../shared/speech-model-contracts' import type { EmbeddingDiagnosticResult, EmbeddingIndexStatus, EmbeddingSettingsSnapshot } from '../shared/embedding-contracts' import type { DocumentOcrAssets, DocumentOcrFailure, DocumentOcrRequest, DocumentOcrResult, DocumentParsingDiagnostic, DocumentParsingSettings, DocumentParsingSnapshot } from '../shared/document-parsing-contracts' import type { AgentRuntimeSelection } from '../shared/runtime-selection-contracts' import type { WeixinBindingSnapshot } from '../shared/weixin-channel-contracts' import type { RemoteChannelActivity } from '../shared/remote-channel-contracts' import type { MagicNoteAnalysisStreamEvent, MagicNoteDraftAnalysis, MagicNoteDetail, MagicNotesSnapshot, MagicTodoItem, MagicTodosSnapshot } from '../shared/magic-notes-contracts' const desktopApi: DesktopApi = { app: { getInfo: () => ipcRenderer.invoke(ipcChannels.appInfo) as Promise, show: async () => { await ipcRenderer.invoke(ipcChannels.appShow) }, hide: async () => { await ipcRenderer.invoke(ipcChannels.appHide) }, minimize: async () => { await ipcRenderer.invoke(ipcChannels.windowMinimize) }, toggleMaximize: async () => { await ipcRenderer.invoke(ipcChannels.windowToggleMaximize) }, close: async () => { await ipcRenderer.invoke(ipcChannels.windowClose) }, isMaximized: () => ipcRenderer.invoke(ipcChannels.windowIsMaximized) as Promise, onMaximizedChanged: (listener) => { const handler = ( _event: Electron.IpcRendererEvent, maximized: boolean ): void => listener(maximized) ipcRenderer.on(ipcChannels.windowMaximizedChanged, handler) return () => ipcRenderer.removeListener( ipcChannels.windowMaximizedChanged, handler ) }, clearLocalData: async () => { await ipcRenderer.invoke(ipcChannels.appClearLocalData) }, onNewConversation: (listener) => { const handler = (): void => listener() ipcRenderer.on(ipcChannels.conversationNew, handler) return () => ipcRenderer.removeListener(ipcChannels.conversationNew, handler) }, onOpenSettings: (listener) => { const handler = (): void => listener() ipcRenderer.on(ipcChannels.settingsOpen, handler) return () => ipcRenderer.removeListener(ipcChannels.settingsOpen, handler) } }, agent: { getStatus: (selection) => ipcRenderer.invoke( ipcChannels.agentStatus, selection ) as Promise, run: async (request: AgentRequest) => { await ipcRenderer.invoke(ipcChannels.agentRun, request) }, cancel: async (requestId: string) => { await ipcRenderer.invoke(ipcChannels.agentCancel, requestId) }, respondApproval: async ( approvalId: string, decision: ApprovalDecision ) => { await ipcRenderer.invoke(ipcChannels.agentApprovalRespond, { approvalId, decision }) }, respondQuestion: async ( questionId: string, answers?: AgentQuestionAnswer[] ) => { await ipcRenderer.invoke(ipcChannels.agentQuestionRespond, { questionId, answers: answers ?? [] }) }, onEvent: (listener) => { const handler = (_event: Electron.IpcRendererEvent, payload: AgentEvent): void => listener(payload) ipcRenderer.on(ipcChannels.agentEvent, handler) return () => ipcRenderer.removeListener(ipcChannels.agentEvent, handler) } }, browser: { interact: async (conversationId: string) => { await ipcRenderer.invoke( ipcChannels.browserInteract, { conversationId } ) }, stop: async (conversationId: string) => { await ipcRenderer.invoke( ipcChannels.browserStop, { conversationId } ) }, onState: (listener) => { const handler = ( _event: Electron.IpcRendererEvent, payload: BrowserLiveState ): void => listener(payload) ipcRenderer.on(ipcChannels.browserState, handler) return () => ipcRenderer.removeListener(ipcChannels.browserState, handler) } }, settings: { getRuntime: () => ipcRenderer.invoke( ipcChannels.runtimeSettingsGet ) as Promise, updateRuntime: (input: RuntimeSettingsInput) => ipcRenderer.invoke( ipcChannels.runtimeSettingsUpdate, input ) as Promise, selectWorkspace: () => ipcRenderer.invoke( ipcChannels.runtimeSettingsSelectWorkspace ) as Promise, detectAgentRuntimes: () => ipcRenderer.invoke( ipcChannels.runtimeSettingsDetect ) as Promise, selectRuntimeFile: (kind: RuntimeFileSelectionKind) => ipcRenderer.invoke( ipcChannels.runtimeSettingsSelectFile, kind ) as Promise, openRuntimeConfig: async (input: RuntimeConfigActionInput) => { await ipcRenderer.invoke( ipcChannels.runtimeSettingsOpenConfig, input ) }, testModelConnection: (profileId: string) => ipcRenderer.invoke( ipcChannels.runtimeSettingsTestModel, profileId ) as Promise, testRuntime: (selection: AgentRuntimeSelection) => ipcRenderer.invoke( ipcChannels.runtimeSettingsTest, selection ) as Promise }, channels: { getSnapshot: () => ipcRenderer.invoke( ipcChannels.channelSettingsGet ) as Promise, apply: (input: ChannelSettingsApply) => ipcRenderer.invoke( ipcChannels.channelSettingsApply, input ) as Promise, testConnection: ( channel: CredentialChannel, settings?: WeComChannelSettingsInput | DingTalkChannelSettingsInput ) => ipcRenderer.invoke(ipcChannels.channelSettingsTest, { channel, settings }) as Promise, getWeixinBinding: () => ipcRenderer.invoke( ipcChannels.weixinBindingGet ) as Promise, startWeixinBinding: () => ipcRenderer.invoke( ipcChannels.weixinBindingStart ) as Promise, submitWeixinVerification: (code: string) => ipcRenderer.invoke( ipcChannels.weixinBindingVerify, { code } ) as Promise, disconnectWeixin: () => ipcRenderer.invoke( ipcChannels.weixinBindingDisconnect ) as Promise, onWeixinBindingChanged: (listener) => { const handler = ( _event: Electron.IpcRendererEvent, snapshot: WeixinBindingSnapshot ): void => listener(snapshot) ipcRenderer.on(ipcChannels.weixinBindingChanged, handler) return () => ipcRenderer.removeListener( ipcChannels.weixinBindingChanged, handler ) }, onRemoteActivity: (listener) => { const handler = ( _event: Electron.IpcRendererEvent, activity: RemoteChannelActivity ): void => listener(activity) ipcRenderer.on(ipcChannels.remoteChannelActivity, handler) return () => ipcRenderer.removeListener( ipcChannels.remoteChannelActivity, handler ) } }, updates: { getSettings: () => ipcRenderer.invoke( ipcChannels.applicationSettingsGet ) as Promise, updateSettings: (input: ApplicationSettingsUpdate) => ipcRenderer.invoke( ipcChannels.applicationSettingsUpdate, input ) as Promise, check: () => ipcRenderer.invoke( ipcChannels.versionCheck ) as Promise, openReleasePage: async () => { await ipcRenderer.invoke(ipcChannels.versionOpenReleasePage) }, onResult: (listener) => { const handler = ( _event: Electron.IpcRendererEvent, result: VersionCheckResult ): void => listener(result) ipcRenderer.on(ipcChannels.versionCheckResult, handler) return () => ipcRenderer.removeListener(ipcChannels.versionCheckResult, handler) } }, releaseNotes: { getPending: () => ipcRenderer.invoke( ipcChannels.releaseNotesGetPending ) as Promise, acknowledge: async (version: string) => { await ipcRenderer.invoke( ipcChannels.releaseNotesAcknowledge, { version } ) } }, speechModels: { getSnapshot: () => ipcRenderer.invoke( ipcChannels.speechModelsGet ) as Promise, install: (modelId: string) => ipcRenderer.invoke( ipcChannels.speechModelsInstall, { modelId } ) as Promise, cancel: (modelId: string) => ipcRenderer.invoke( ipcChannels.speechModelsCancel, { modelId } ) as Promise, remove: (modelId: string) => ipcRenderer.invoke( ipcChannels.speechModelsRemove, { modelId } ) as Promise, select: (modelId: string | null) => ipcRenderer.invoke( ipcChannels.speechModelsSelect, { modelId } ) as Promise, importArchive: (modelId: string) => ipcRenderer.invoke( ipcChannels.speechModelsImportArchive, { modelId } ) as Promise, exportArchive: (modelId: string) => ipcRenderer.invoke( ipcChannels.speechModelsExportArchive, { modelId } ) as Promise, openRepository: async (modelId: string) => { await ipcRenderer.invoke( ipcChannels.speechModelsOpenRepository, { modelId } ) }, openModelsDirectory: async () => { await ipcRenderer.invoke(ipcChannels.speechModelsOpenDirectory) } }, speech: { transcribe: (input: SpeechTranscriptionInput) => ipcRenderer.invoke( ipcChannels.speechTranscribe, input ) as Promise, cancel: (requestId: string) => ipcRenderer.invoke( ipcChannels.speechTranscriptionCancel, requestId ) as Promise }, embeddings: { getSnapshot: () => ipcRenderer.invoke( ipcChannels.embeddingSettingsGet ) as Promise, diagnose: () => ipcRenderer.invoke( ipcChannels.embeddingDiagnose ) as Promise, rebuild: () => ipcRenderer.invoke( ipcChannels.embeddingIndexRebuild ) as Promise, cancel: (jobId: string) => ipcRenderer.invoke( ipcChannels.embeddingIndexCancel, { jobId } ) as Promise, onStatus: (listener) => { const handler = ( _event: Electron.IpcRendererEvent, status: EmbeddingIndexStatus ): void => listener(status) ipcRenderer.on(ipcChannels.embeddingIndexStatusChanged, handler) return () => ipcRenderer.removeListener( ipcChannels.embeddingIndexStatusChanged, handler ) } }, documentParsing: { getSnapshot: () => ipcRenderer.invoke( ipcChannels.documentParsingGet ) as Promise, update: (input: DocumentParsingSettings) => ipcRenderer.invoke( ipcChannels.documentParsingUpdate, input ) as Promise, test: () => ipcRenderer.invoke( ipcChannels.documentParsingTest ) as Promise, installOcrModel: (modelId: string) => ipcRenderer.invoke( ipcChannels.documentOcrModelsInstall, { modelId } ) as Promise, cancelOcrModelOperation: (modelId: string) => ipcRenderer.invoke( ipcChannels.documentOcrModelsCancel, { modelId } ) as Promise, removeOcrModel: (modelId: string) => ipcRenderer.invoke( ipcChannels.documentOcrModelsRemove, { modelId } ) as Promise, importOcrModelArchive: (modelId: string) => ipcRenderer.invoke( ipcChannels.documentOcrModelsImportArchive, { modelId } ) as Promise, exportOcrModelArchive: (modelId: string) => ipcRenderer.invoke( ipcChannels.documentOcrModelsExportArchive, { modelId } ) as Promise, openOcrModelRepository: async (modelId: string) => { await ipcRenderer.invoke( ipcChannels.documentOcrModelsOpenRepository, { modelId } ) }, openOcrModelsDirectory: async () => { await ipcRenderer.invoke( ipcChannels.documentOcrModelsOpenDirectory ) }, getOcrAssets: (modelId: string) => ipcRenderer.invoke( ipcChannels.documentParsingOcrAssets, { modelId } ) as Promise, respondOcr: async ( response: DocumentOcrResult | DocumentOcrFailure ) => { await ipcRenderer.invoke( ipcChannels.documentParsingOcrRespond, response ) }, onOcrRequest: (listener) => { const handler = ( _event: Electron.IpcRendererEvent, request: DocumentOcrRequest ): void => listener(request) ipcRenderer.on(ipcChannels.documentParsingOcrRequest, handler) return () => ipcRenderer.removeListener( ipcChannels.documentParsingOcrRequest, handler ) }, onOcrCancel: (listener) => { const handler = ( _event: Electron.IpcRendererEvent, requestId: string ): void => listener(requestId) ipcRenderer.on(ipcChannels.documentParsingOcrCancel, handler) return () => ipcRenderer.removeListener( ipcChannels.documentParsingOcrCancel, handler ) } }, projects: { list: (includeArchived = false) => ipcRenderer.invoke( ipcChannels.projectsList, includeArchived ) as Promise, create: (input: ProjectCreateInput) => ipcRenderer.invoke( ipcChannels.projectsCreate, input ) as Promise, update: (projectId: string, input: ProjectCreateInput) => ipcRenderer.invoke( ipcChannels.projectsUpdate, { projectId, input } ) as Promise, setArchived: async (projectId: string, archived: boolean) => { await ipcRenderer.invoke(ipcChannels.projectsSetArchived, { projectId, archived }) }, delete: async (projectId: string, confirmation: string) => { await ipcRenderer.invoke(ipcChannels.projectsDelete, { projectId, confirmation }) } }, conversations: { list: () => ipcRenderer.invoke( ipcChannels.conversationsList ) as Promise, replace: async (conversations: ConversationSnapshot[]) => { await ipcRenderer.invoke( ipcChannels.conversationsReplace, conversations ) }, onChanged: (listener) => { const handler = (): void => listener() ipcRenderer.on(ipcChannels.conversationsChanged, handler) return () => ipcRenderer.removeListener( ipcChannels.conversationsChanged, handler ) } }, workspace: { getChanges: (projectId: string) => ipcRenderer.invoke( ipcChannels.workspaceChangesGet, projectId ) as Promise, listDirectory: (projectId: string, path: string) => ipcRenderer.invoke(ipcChannels.workspaceDirectoryList, { projectId, path }) as Promise, readFile: (projectId: string, path: string) => ipcRenderer.invoke(ipcChannels.workspaceFileRead, { projectId, path }) as Promise, openPath: async ( projectId: string, path: string, type: 'file' | 'directory' ) => { await ipcRenderer.invoke(ipcChannels.workspacePathOpen, { projectId, path, type }) } }, tasks: { list: () => ipcRenderer.invoke(ipcChannels.tasksList) as Promise, setStatus: async ( taskId: string, status: 'completed' | 'cancelled' ) => { await ipcRenderer.invoke(ipcChannels.tasksSetStatus, { taskId, status }) } }, usage: { getTokenSummary: () => ipcRenderer.invoke( ipcChannels.tokenUsageSummary ) as Promise }, artifacts: { list: (projectId?: string) => ipcRenderer.invoke( ipcChannels.artifactsList, projectId ) as Promise, get: (artifactId: string) => ipcRenderer.invoke( ipcChannels.artifactsGet, artifactId ) as Promise, importFiles: (projectId?: string) => ipcRenderer.invoke( ipcChannels.artifactsImportFiles, projectId ) as Promise }, memory: { list: (scopeId?: string) => ipcRenderer.invoke( ipcChannels.memoryList, scopeId ) as Promise, create: (input: MemoryCreateInput) => ipcRenderer.invoke( ipcChannels.memoryCreate, input ) as Promise, setStatus: async ( memoryId: string, status: AssistantMemory['status'] ) => { await ipcRenderer.invoke(ipcChannels.memorySetStatus, { memoryId, status }) }, remove: async (memoryId: string) => { await ipcRenderer.invoke(ipcChannels.memoryRemove, memoryId) } }, schedules: { list: (projectId?: string) => ipcRenderer.invoke( ipcChannels.schedulesList, projectId ) as Promise, create: (input: ScheduleCreateInput) => ipcRenderer.invoke( ipcChannels.schedulesCreate, input ) as Promise, setEnabled: async (scheduleId: string, enabled: boolean) => { await ipcRenderer.invoke(ipcChannels.schedulesSetEnabled, { scheduleId, enabled }) }, remove: async (scheduleId: string) => { await ipcRenderer.invoke(ipcChannels.schedulesRemove, scheduleId) }, runNow: async (scheduleId: string) => { await ipcRenderer.invoke(ipcChannels.schedulesRunNow, scheduleId) } }, heartbeats: { list: (projectId?: string) => ipcRenderer.invoke(ipcChannels.heartbeatsList, { projectId }) as Promise, create: (input: HeartbeatCreateInput) => ipcRenderer.invoke( ipcChannels.heartbeatsCreate, input ) as Promise, update: (heartbeatId: string, input: HeartbeatUpdateInput) => ipcRenderer.invoke(ipcChannels.heartbeatsUpdate, { id: heartbeatId, config: input }) as Promise, setPaused: async (heartbeatId: string, paused: boolean) => { await ipcRenderer.invoke(ipcChannels.heartbeatsSetPaused, { id: heartbeatId, paused }) }, remove: async (heartbeatId: string) => { await ipcRenderer.invoke(ipcChannels.heartbeatsRemove, { id: heartbeatId }) }, runNow: (heartbeatId: string) => ipcRenderer.invoke(ipcChannels.heartbeatsRunNow, { id: heartbeatId, idempotencyKey: crypto.randomUUID() }) as Promise, history: (heartbeatId?: string) => ipcRenderer.invoke(ipcChannels.heartbeatsHistory, { configId: heartbeatId, limit: 200 }) as Promise<{ runs: AssistantHeartbeatRun[] entries: AssistantHeartbeatEntry[] }> }, experts: { list: () => ipcRenderer.invoke( ipcChannels.expertsList ) as Promise, create: (input: ExpertCreateInput) => ipcRenderer.invoke( ipcChannels.expertsCreate, input ) as Promise, update: (expertId: string, input: ExpertUpdateInput) => ipcRenderer.invoke(ipcChannels.expertsUpdate, { expertId, input }) as Promise, remove: async (expertId: string) => { await ipcRenderer.invoke(ipcChannels.expertsRemove, expertId) } }, capabilities: { getSnapshot: () => ipcRenderer.invoke( ipcChannels.capabilitiesSnapshot ) as Promise, importSkill: (kind) => ipcRenderer.invoke( ipcChannels.capabilitiesImportSkill, kind ) as Promise, removeSkill: (skillId) => ipcRenderer.invoke( ipcChannels.capabilitiesRemoveSkill, skillId ) as Promise, setSkillEnabled: (skillId, enabled) => ipcRenderer.invoke(ipcChannels.capabilitiesToggleSkill, { skillId, enabled }) as Promise, setSkillAssignments: (skillId, assignments) => ipcRenderer.invoke(ipcChannels.capabilitiesAssignSkill, { skillId, assignments }) as Promise, saveMcpServer: (serverId, input) => ipcRenderer.invoke(ipcChannels.capabilitiesSaveMcp, { serverId, input }) as Promise, removeMcpServer: (serverId) => ipcRenderer.invoke( ipcChannels.capabilitiesRemoveMcp, serverId ) as Promise, testMcpServer: (serverId) => ipcRenderer.invoke( ipcChannels.capabilitiesTestMcp, serverId ) as Promise, setWebSearchEnabled: (enabled: boolean) => ipcRenderer.invoke( ipcChannels.capabilitiesToggleWebSearch, enabled ) as Promise, testWebSearch: () => ipcRenderer.invoke( ipcChannels.capabilitiesTestWebSearch ) as Promise, setComputerCapabilityEnabled: ( capabilityId: ComputerCapabilityId, enabled: boolean ) => ipcRenderer.invoke(ipcChannels.capabilitiesToggleComputer, { capabilityId, enabled }) as Promise, setComputerCapabilityBrowserProfile: ( capabilityId: ComputerCapabilityId, browserProfileId: string | null ) => ipcRenderer.invoke(ipcChannels.capabilitiesConfigureComputer, { capabilityId, browserProfileId }) as Promise, diagnoseComputerCapability: (capabilityId: ComputerCapabilityId) => ipcRenderer.invoke( ipcChannels.capabilitiesDiagnoseComputer, capabilityId ) as Promise, createBrowserProfile: (input: BrowserProfileCreateInput) => ipcRenderer.invoke( ipcChannels.capabilitiesCreateBrowserProfile, input ) as Promise, renameBrowserProfile: (input: BrowserProfileRenameInput) => ipcRenderer.invoke( ipcChannels.capabilitiesRenameBrowserProfile, input ) as Promise, setDefaultBrowserProfile: (profileId: string) => ipcRenderer.invoke(ipcChannels.capabilitiesDefaultBrowserProfile, { profileId }) as Promise, removeBrowserProfile: (profileId: string) => ipcRenderer.invoke(ipcChannels.capabilitiesRemoveBrowserProfile, { profileId }) as Promise }, context: { selectFiles: () => ipcRenderer.invoke( ipcChannels.contextSelectFiles ) as Promise, onFileSelectionProgress: (listener) => { const handler = ( _event: Electron.IpcRendererEvent, progress: ContextFileSelectionProgress ): void => listener(progress) ipcRenderer.on(ipcChannels.contextFileSelectionProgress, handler) return () => ipcRenderer.removeListener( ipcChannels.contextFileSelectionProgress, handler ) }, addPastedImage: (input: PastedImageInput) => ipcRenderer.invoke( ipcChannels.contextAddPastedImage, input ) as Promise, captureScreen: () => ipcRenderer.invoke( ipcChannels.contextCaptureScreen ) as Promise, listWindows: () => ipcRenderer.invoke( ipcChannels.contextListWindows ) as Promise, captureWindow: (sourceId) => ipcRenderer.invoke( ipcChannels.contextCaptureWindow, { sourceId } ) as Promise, readClipboard: () => ipcRenderer.invoke( ipcChannels.contextReadClipboard ) as Promise, remove: async (contextId: string) => { await ipcRenderer.invoke(ipcChannels.contextRemove, contextId) } }, magicNotes: { list: () => ipcRenderer.invoke( ipcChannels.magicNotesList ) as Promise, get: (noteId: string) => ipcRenderer.invoke(ipcChannels.magicNotesGet, { noteId }) as Promise, create: (input) => ipcRenderer.invoke( ipcChannels.magicNotesCreate, input ) as Promise, update: (input) => ipcRenderer.invoke( ipcChannels.magicNotesUpdate, input ) as Promise, remove: async (noteId: string) => { await ipcRenderer.invoke(ipcChannels.magicNotesDelete, { noteId }) }, createEntry: (input) => ipcRenderer.invoke( ipcChannels.magicNotesCreateEntry, input ) as Promise, updateEntry: (input) => ipcRenderer.invoke( ipcChannels.magicNotesUpdateEntry, input ) as Promise, removeEntry: (entryId: string) => ipcRenderer.invoke(ipcChannels.magicNotesDeleteEntry, { entryId }) as Promise, analyze: (entryId, options) => ipcRenderer.invoke(ipcChannels.magicNotesAnalyze, { entryId, ...options }) as Promise, analyzeDraft: (content, options) => ipcRenderer.invoke( ipcChannels.magicNotesAnalyzeDraft, { content, ...options } ) as Promise, listTodos: () => ipcRenderer.invoke( ipcChannels.magicTodosList ) as Promise, updateTodo: (input) => ipcRenderer.invoke( ipcChannels.magicTodosUpdate, input ) as Promise, analyzeTodo: (todoId, options) => ipcRenderer.invoke(ipcChannels.magicTodosAnalyze, { todoId, ...options }) as Promise, onAnalysisEvent: (listener) => { const handler = ( _event: Electron.IpcRendererEvent, payload: MagicNoteAnalysisStreamEvent ): void => listener(payload) ipcRenderer.on(ipcChannels.magicNotesAnalysisEvent, handler) return () => ipcRenderer.removeListener( ipcChannels.magicNotesAnalysisEvent, handler ) } }, knowledge: { getSnapshot: (libraryId?: string) => ipcRenderer.invoke( ipcChannels.knowledgeSnapshot, libraryId ) as Promise, createLibrary: (input) => ipcRenderer.invoke( ipcChannels.knowledgeCreateLibrary, input ) as Promise, updateLibrary: async (libraryId, update) => { await ipcRenderer.invoke(ipcChannels.knowledgeUpdateLibrary, { libraryId, ...update }) }, deleteLibrary: async (libraryId) => { await ipcRenderer.invoke( ipcChannels.knowledgeDeleteLibrary, libraryId ) }, reextractGraph: (libraryId) => ipcRenderer.invoke( ipcChannels.knowledgeReextractGraph, libraryId ) as Promise, selectFiles: async (libraryId, graphStrategy) => { await ipcRenderer.invoke(ipcChannels.knowledgeSelectFiles, { libraryId, graphStrategy }) }, selectDirectory: async (libraryId, graphStrategy) => { await ipcRenderer.invoke( ipcChannels.knowledgeSelectDirectory, { libraryId, graphStrategy } ) }, importDroppedFiles: async (libraryId, files, graphStrategy) => { const paths = files .map((file) => webUtils.getPathForFile(file)) .filter(Boolean) await ipcRenderer.invoke(ipcChannels.knowledgeImportPaths, { libraryId, paths, graphStrategy }) }, importUrl: async (libraryId, url, graphStrategy) => { await ipcRenderer.invoke(ipcChannels.knowledgeImportUrl, { libraryId, url, graphStrategy }) }, syncSource: async (sourceId) => { await ipcRenderer.invoke(ipcChannels.knowledgeSyncSource, sourceId) }, pauseSource: async (sourceId) => { await ipcRenderer.invoke(ipcChannels.knowledgePauseSource, sourceId) }, retrySource: async (sourceId) => { await ipcRenderer.invoke(ipcChannels.knowledgeRetrySource, sourceId) }, removeSource: async (sourceId) => { await ipcRenderer.invoke(ipcChannels.knowledgeRemoveSource, sourceId) }, search: (libraryIds, query) => ipcRenderer.invoke(ipcChannels.knowledgeSearch, { libraryIds, query }) as Promise, createEntity: async (libraryId, input) => { await ipcRenderer.invoke(ipcChannels.knowledgeCreateEntity, { libraryId, input }) }, updateEntity: async (entityId, update) => { await ipcRenderer.invoke(ipcChannels.knowledgeUpdateEntity, { entityId, update }) }, moveEntity: async (entityId, position) => { await ipcRenderer.invoke(ipcChannels.knowledgeMoveEntity, { entityId, position }) }, deleteEntity: async (entityId) => { await ipcRenderer.invoke(ipcChannels.knowledgeDeleteEntity, entityId) }, mergeEntities: async (sourceEntityId, targetEntityId) => { await ipcRenderer.invoke(ipcChannels.knowledgeMergeEntities, { sourceEntityId, targetEntityId }) }, createRelation: async (libraryId, input) => { await ipcRenderer.invoke(ipcChannels.knowledgeCreateRelation, { libraryId, input }) }, updateRelation: async (relationId, input) => { await ipcRenderer.invoke(ipcChannels.knowledgeUpdateRelation, { relationId, input }) }, deleteRelation: async (relationId) => { await ipcRenderer.invoke( ipcChannels.knowledgeDeleteRelation, relationId ) } } } contextBridge.exposeInMainWorld('goodbuddy', desktopApi)