Files
WeFlow/electron/preload.ts
T
2026-09-28 21:16:43 +08:00

803 lines
44 KiB
TypeScript

import { contextBridge, ipcRenderer } from 'electron'
type CloseConfirmPayload = {
canMinimizeToTray: boolean
restoreMethod?: 'tray' | 'dock'
}
// 暴露给渲染进程的 API
contextBridge.exposeInMainWorld('electronAPI', {
// 配置
config: {
get: (key: string) => ipcRenderer.invoke('config:get', key),
set: (key: string, value: any) => ipcRenderer.invoke('config:set', key, value),
clear: () => ipcRenderer.invoke('config:clear'),
onChanged: (callback: (event: { key: string }) => void) => {
const listener = (_: unknown, event: { key: string }) => callback(event)
ipcRenderer.on('config:changed', listener)
return () => ipcRenderer.removeListener('config:changed', listener)
}
},
// 通知
notification: {
show: (data: any) => ipcRenderer.invoke('notification:show', data),
close: () => ipcRenderer.invoke('notification:close'),
click: (payload: any) => ipcRenderer.send('notification-clicked', payload),
ready: (rendererSessionId: string) => ipcRenderer.send('notification:ready', rendererSessionId),
received: (deliveryId: string) => ipcRenderer.send('notification:received', deliveryId),
resize: (width: number, height: number) => ipcRenderer.send('notification:resize', { width, height }),
// 原生玻璃面板:卡片实测几何上报 / 退场淡出 / 亮度带回调(Windows 原生模式专用)
glassRect: (payload: any) => ipcRenderer.send('notification:glassRect', payload),
glassHide: () => ipcRenderer.send('notification:glassHide'),
onLuma: (callback: (bands: any) => void) => {
const listener = (_: any, bands: any) => callback(bands)
ipcRenderer.on('notification:luma', listener)
return () => ipcRenderer.removeListener('notification:luma', listener)
},
onShow: (callback: (event: any, data: any) => void) => {
ipcRenderer.on('notification:show', callback)
return () => ipcRenderer.removeListener('notification:show', callback)
},
// 监听原本发送出来的navigate-to-session事件,跳转到具体的会话
onNavigateToSession: (callback: (sessionId: string) => void) => {
const listener = (_: any, sessionId: string) => callback(sessionId)
ipcRenderer.on('navigate-to-session', listener)
return () => ipcRenderer.removeListener('navigate-to-session', listener)
},
onNavigateToRoute: (callback: (route: string) => void) => {
const listener = (_: any, route: string) => callback(route)
ipcRenderer.on('navigate-to-route', listener)
return () => ipcRenderer.removeListener('navigate-to-route', listener)
}
},
// 认证
auth: {
hello: (message?: string) => ipcRenderer.invoke('auth:hello', message),
verifyEnabled: () => ipcRenderer.invoke('auth:verifyEnabled'),
unlock: (password: string) => ipcRenderer.invoke('auth:unlock', password),
enableLock: (password: string) => ipcRenderer.invoke('auth:enableLock', password),
disableLock: (password: string) => ipcRenderer.invoke('auth:disableLock', password),
changePassword: (oldPassword: string, newPassword: string) => ipcRenderer.invoke('auth:changePassword', oldPassword, newPassword),
setHelloSecret: (password: string) => ipcRenderer.invoke('auth:setHelloSecret', password),
clearHelloSecret: () => ipcRenderer.invoke('auth:clearHelloSecret'),
isLockMode: () => ipcRenderer.invoke('auth:isLockMode')
},
// 对话框
dialog: {
openFile: (options: any) => ipcRenderer.invoke('dialog:openFile', options),
openDirectory: (options: any) => ipcRenderer.invoke('dialog:openDirectory', options),
saveFile: (options: any) => ipcRenderer.invoke('dialog:saveFile', options)
},
// Shell
shell: {
openPath: (path: string) => ipcRenderer.invoke('shell:openPath', path),
openExternal: (url: string) => ipcRenderer.invoke('shell:openExternal', url)
},
// App
app: {
getDownloadsPath: () => ipcRenderer.invoke('app:getDownloadsPath'),
getVersion: () => ipcRenderer.invoke('app:getVersion'),
getLaunchAtStartupStatus: () => ipcRenderer.invoke('app:getLaunchAtStartupStatus'),
setLaunchAtStartup: (enabled: boolean) => ipcRenderer.invoke('app:setLaunchAtStartup', enabled),
checkForUpdates: () => ipcRenderer.invoke('app:checkForUpdates'),
downloadAndInstall: () => ipcRenderer.invoke('app:downloadAndInstall'),
ignoreUpdate: (version: string) => ipcRenderer.invoke('app:ignoreUpdate', version),
onDownloadProgress: (callback: (progress: any) => void) => {
ipcRenderer.on('app:downloadProgress', (_, progress) => callback(progress))
return () => ipcRenderer.removeAllListeners('app:downloadProgress')
},
onUpdateAvailable: (callback: (info: { version: string; releaseNotes: string }) => void) => {
ipcRenderer.on('app:updateAvailable', (_, info) => callback(info))
return () => ipcRenderer.removeAllListeners('app:updateAvailable')
},
},
// 日志
log: {
getPath: () => ipcRenderer.invoke('log:getPath'),
read: () => ipcRenderer.invoke('log:read'),
clear: () => ipcRenderer.invoke('log:clear'),
debug: (data: any) => ipcRenderer.send('log:debug', data)
},
diagnostics: {
getExportCardLogs: (options?: { limit?: number }) =>
ipcRenderer.invoke('diagnostics:getExportCardLogs', options),
clearExportCardLogs: () =>
ipcRenderer.invoke('diagnostics:clearExportCardLogs'),
recordResourceStats: (payload: any) =>
ipcRenderer.invoke('diagnostics:recordResourceStats', payload),
getResourceStats: (options?: { limit?: number }) =>
ipcRenderer.invoke('diagnostics:getResourceStats', options),
clearResourceStats: () =>
ipcRenderer.invoke('diagnostics:clearResourceStats'),
exportExportCardLogs: (payload: { filePath: string; frontendLogs?: unknown[] }) =>
ipcRenderer.invoke('diagnostics:exportExportCardLogs', payload)
},
// 窗口控制
window: {
minimize: () => ipcRenderer.send('window:minimize'),
maximize: () => ipcRenderer.send('window:maximize'),
isMaximized: () => ipcRenderer.invoke('window:isMaximized'),
onMaximizeStateChanged: (callback: (isMaximized: boolean) => void) => {
const listener = (_: unknown, isMaximized: boolean) => callback(isMaximized)
ipcRenderer.on('window:maximizeStateChanged', listener)
return () => ipcRenderer.removeListener('window:maximizeStateChanged', listener)
},
close: () => ipcRenderer.send('window:close'),
onCloseConfirmRequested: (callback: (payload: CloseConfirmPayload) => void) => {
const listener = (_: unknown, payload: CloseConfirmPayload) => callback(payload)
ipcRenderer.on('window:confirmCloseRequested', listener)
return () => ipcRenderer.removeListener('window:confirmCloseRequested', listener)
},
respondCloseConfirm: (action: 'tray' | 'quit' | 'cancel') =>
ipcRenderer.invoke('window:respondCloseConfirm', action),
openAgreementWindow: () => ipcRenderer.invoke('window:openAgreementWindow'),
completeOnboarding: () => ipcRenderer.invoke('window:completeOnboarding'),
openOnboardingWindow: (options?: { mode?: 'add-account' }) => ipcRenderer.invoke('window:openOnboardingWindow', options),
openErrorReferenceWindow: (query?: string) => ipcRenderer.invoke('window:openErrorReferenceWindow', query),
setTitleBarOverlay: (options: { symbolColor: string }) => ipcRenderer.send('window:setTitleBarOverlay', options),
openVideoPlayerWindow: (videoPath: string, videoWidth?: number, videoHeight?: number) =>
ipcRenderer.invoke('window:openVideoPlayerWindow', videoPath, videoWidth, videoHeight),
resizeToFitVideo: (videoWidth: number, videoHeight: number) =>
ipcRenderer.invoke('window:resizeToFitVideo', videoWidth, videoHeight),
openImageViewerWindow: (imagePath: string, liveVideoPath?: string) =>
ipcRenderer.invoke('window:openImageViewerWindow', imagePath, liveVideoPath),
openChatHistoryWindow: (sessionId: string, messageId: number) =>
ipcRenderer.invoke('window:openChatHistoryWindow', sessionId, messageId),
openChatHistoryPayloadWindow: (payload: { sessionId: string; title?: string; recordList: any[]; rawContent?: string }) =>
ipcRenderer.invoke('window:openChatHistoryPayloadWindow', payload),
getChatHistoryPayload: (payloadId: string) =>
ipcRenderer.invoke('window:getChatHistoryPayload', payloadId),
openSessionChatWindow: (
sessionId: string,
options?: {
source?: 'chat' | 'export'
initialDisplayName?: string
initialAvatarUrl?: string
initialContactType?: 'friend' | 'group' | 'official' | 'former_friend' | 'blocked' | 'other'
}
) =>
ipcRenderer.invoke('window:openSessionChatWindow', sessionId, options),
openPersonaChatWindow: (_sessionId: string) => Promise.resolve(false)
},
// 账号目录(纯手动输入,只做存在性校验)
account: {
resolveDir: (dbPath: string, accountId?: string) =>
ipcRenderer.invoke('account:resolveDir', dbPath, accountId)
},
// WCDB 数据库
wcdb: {
testConnection: (dbPath: string, hexKey: string, accountId: string) =>
ipcRenderer.invoke('wcdb:testConnection', dbPath, hexKey, accountId),
open: (dbPath: string, hexKey: string, accountId: string) =>
ipcRenderer.invoke('wcdb:open', dbPath, hexKey, accountId),
close: () => ipcRenderer.invoke('wcdb:close'),
},
backup: {
create: (payload: { outputPath: string; options?: { includeImages?: boolean; includeVideos?: boolean; includeFiles?: boolean } }) => ipcRenderer.invoke('backup:create', payload),
inspect: (payload: { archivePath: string }) => ipcRenderer.invoke('backup:inspect', payload),
restore: (payload: { archivePath: string }) => ipcRenderer.invoke('backup:restore', payload),
onProgress: (callback: (progress: any) => void) => {
const listener = (_: unknown, progress: any) => callback(progress)
ipcRenderer.on('backup:progress', listener)
return () => ipcRenderer.removeListener('backup:progress', listener)
}
},
// 密钥获取
key: {
autoGetDbKey: (dbPath?: string, accountId?: string, internalDbKeyHex?: string) => ipcRenderer.invoke('key:autoGetDbKey', dbPath, accountId, internalDbKeyHex),
trace: (stage: string, data?: any) => ipcRenderer.send('keyflow:trace', stage, data),
autoGetImageKey: (manualDir?: string, accountId?: string) => ipcRenderer.invoke('key:autoGetImageKey', manualDir, accountId),
scanImageKeyFromMemory: (userDir: string) => ipcRenderer.invoke('key:scanImageKeyFromMemory', userDir),
onDbKeyStatus: (callback: (payload: { message: string; level: number }) => void) => {
ipcRenderer.on('key:dbKeyStatus', (_, payload) => callback(payload))
return () => ipcRenderer.removeAllListeners('key:dbKeyStatus')
},
onImageKeyStatus: (callback: (payload: { message: string }) => void) => {
ipcRenderer.on('key:imageKeyStatus', (_, payload) => callback(payload))
return () => ipcRenderer.removeAllListeners('key:imageKeyStatus')
}
},
// 聊天
chat: {
connect: () => ipcRenderer.invoke('chat:connect'),
getSessions: () => ipcRenderer.invoke('chat:getSessions'),
refreshSessionContactDisplayNames: (usernames: string[]) =>
ipcRenderer.invoke('chat:refreshSessionContactDisplayNames', usernames),
getMentionTargets: (offset?: number, limit?: number, keyword?: string) =>
ipcRenderer.invoke('chat:getMentionTargets', offset, limit, keyword),
markAllSessionsRead: () => ipcRenderer.invoke('chat:markAllSessionsRead'),
reorderSessionsByTime: () => ipcRenderer.invoke('chat:reorderSessionsByTime'),
getAntiRevokeSessions: () => ipcRenderer.invoke('chat:getAntiRevokeSessions'),
getSessionStatuses: (usernames: string[]) => ipcRenderer.invoke('chat:getSessionStatuses', usernames),
getExportTabCounts: () => ipcRenderer.invoke('chat:getExportTabCounts'),
getContactTypeCounts: () => ipcRenderer.invoke('chat:getContactTypeCounts'),
getSessionMessageCounts: (sessionIds: string[], options?: { preferHintCache?: boolean; bypassSessionCache?: boolean }) => ipcRenderer.invoke('chat:getSessionMessageCounts', sessionIds, options),
enrichSessionsContactInfo: (
usernames: string[],
options?: { skipDisplayName?: boolean; onlyMissingAvatar?: boolean; background?: boolean }
) => ipcRenderer.invoke('chat:enrichSessionsContactInfo', usernames, options),
getMessages: (sessionId: string, offset?: number, limit?: number, startTime?: number, endTime?: number, ascending?: boolean) =>
ipcRenderer.invoke('chat:getMessages', sessionId, offset, limit, startTime, endTime, ascending),
getRelationshipJourneyMoments: (sessionId: string, maxMessages?: number, forceRefresh?: boolean) =>
ipcRenderer.invoke('chat:getRelationshipJourneyMoments', sessionId, maxMessages, forceRefresh),
cancelRelationshipJourneyMoments: (sessionId: string) =>
ipcRenderer.invoke('chat:cancelRelationshipJourneyMoments', sessionId),
getLatestMessages: (sessionId: string, limit?: number) =>
ipcRenderer.invoke('chat:getLatestMessages', sessionId, limit),
getNewMessages: (sessionId: string, minTime: number, limit?: number, cursor?: {
createTime?: number
sortSeq?: number
localId?: number
serverId?: number | string
serverIdRaw?: string
}) =>
ipcRenderer.invoke('chat:getNewMessages', sessionId, minTime, limit, cursor),
getContact: (username: string) => ipcRenderer.invoke('chat:getContact', username),
getContactAvatar: (username: string, chatroomId?: string) => ipcRenderer.invoke('chat:getContactAvatar', username, chatroomId),
updateMessage: (sessionId: string, localId: number, createTime: number, newContent: string) =>
ipcRenderer.invoke('chat:updateMessage', sessionId, localId, createTime, newContent),
insertTextMessage: (payload: { sessionId: string; content: string; status: number; createTime?: number }) =>
ipcRenderer.invoke('chat:insertTextMessage', payload),
deleteMessage: (sessionId: string, localId: number, createTime: number, dbPathHint?: string) =>
ipcRenderer.invoke('chat:deleteMessage', sessionId, localId, createTime, dbPathHint),
checkAntiRevokeTriggers: (sessionIds: string[]) =>
ipcRenderer.invoke('chat:checkAntiRevokeTriggers', sessionIds),
installAntiRevokeTriggers: (sessionIds: string[]) =>
ipcRenderer.invoke('chat:installAntiRevokeTriggers', sessionIds),
uninstallAntiRevokeTriggers: (sessionIds: string[]) =>
ipcRenderer.invoke('chat:uninstallAntiRevokeTriggers', sessionIds),
resolveTransferDisplayNames: (chatroomId: string, payerUsername: string, receiverUsername: string) =>
ipcRenderer.invoke('chat:resolveTransferDisplayNames', chatroomId, payerUsername, receiverUsername),
getMyAvatarUrl: () => ipcRenderer.invoke('chat:getMyAvatarUrl'),
downloadEmoji: (cdnUrl: string, md5?: string) => ipcRenderer.invoke('chat:downloadEmoji', cdnUrl, md5),
getCachedMessages: (sessionId: string) => ipcRenderer.invoke('chat:getCachedMessages', sessionId),
clearCurrentAccountData: (options: { clearCache?: boolean; clearExports?: boolean }) =>
ipcRenderer.invoke('chat:clearCurrentAccountData', options),
close: () => ipcRenderer.invoke('chat:close'),
getSessionDetail: (sessionId: string) => ipcRenderer.invoke('chat:getSessionDetail', sessionId),
getSessionDetailFast: (sessionId: string) => ipcRenderer.invoke('chat:getSessionDetailFast', sessionId),
getSessionDetailExtra: (sessionId: string) => ipcRenderer.invoke('chat:getSessionDetailExtra', sessionId),
getExportSessionStats: (
sessionIds: string[],
options?: {
includeRelations?: boolean
forceRefresh?: boolean
allowStaleCache?: boolean
preferAccurateSpecialTypes?: boolean
cacheOnly?: boolean
beginTimestamp?: number
endTimestamp?: number
includeMessageDateCounts?: boolean
}
) => ipcRenderer.invoke('chat:getExportSessionStats', sessionIds, options),
getGroupMyMessageCountHint: (chatroomId: string) =>
ipcRenderer.invoke('chat:getGroupMyMessageCountHint', chatroomId),
getImageData: (sessionId: string, msgId: string) => ipcRenderer.invoke('chat:getImageData', sessionId, msgId),
getVoiceData: (sessionId: string, msgId: string, createTime?: number, serverId?: string | number) =>
ipcRenderer.invoke('chat:getVoiceData', sessionId, msgId, createTime, serverId),
getAllVoiceMessages: (sessionId: string) => ipcRenderer.invoke('chat:getAllVoiceMessages', sessionId),
getAllImageMessages: (sessionId: string) => ipcRenderer.invoke('chat:getAllImageMessages', sessionId),
getMessageDates: (sessionId: string) => ipcRenderer.invoke('chat:getMessageDates', sessionId),
getMessageDateCounts: (sessionId: string) => ipcRenderer.invoke('chat:getMessageDateCounts', sessionId),
getResourceMessages: (options?: {
sessionId?: string
types?: Array<'image' | 'video' | 'voice' | 'file'>
beginTimestamp?: number
endTimestamp?: number
limit?: number
offset?: number
}) => ipcRenderer.invoke('chat:getResourceMessages', options),
getMediaStream: (options?: {
sessionId?: string
mediaType?: 'image' | 'video' | 'all'
beginTimestamp?: number
endTimestamp?: number
limit?: number
offset?: number
}) => ipcRenderer.invoke('chat:getMediaStream', options),
resolveVoiceCache: (sessionId: string, msgId: string) => ipcRenderer.invoke('chat:resolveVoiceCache', sessionId, msgId),
getVoiceTranscript: (sessionId: string, msgId: string, createTime?: number, serverId?: string | number, messageKey?: string) => ipcRenderer.invoke('chat:getVoiceTranscript', sessionId, msgId, createTime, serverId, messageKey),
onVoiceTranscriptPartial: (callback: (payload: { sessionId?: string; msgId: string; createTime?: number; text: string }) => void) => {
const listener = (_: any, payload: { sessionId?: string; msgId: string; createTime?: number; text: string }) => callback(payload)
ipcRenderer.on('chat:voiceTranscriptPartial', listener)
return () => ipcRenderer.removeListener('chat:voiceTranscriptPartial', listener)
},
getContacts: (options?: { lite?: boolean }) => ipcRenderer.invoke('chat:getContacts', options),
getMessage: (sessionId: string, localId: number) =>
ipcRenderer.invoke('chat:getMessage', sessionId, localId),
getMessageWindowForJump: (
sessionId: string,
target: { localId?: number; createTime: number; messageKey?: string; messageLocator?: string },
totalContextCount?: number
) => ipcRenderer.invoke('chat:getMessageWindowForJump', sessionId, target, totalContextCount),
searchMessages: (keyword: string, sessionId?: string, limit?: number, offset?: number, beginTimestamp?: number, endTimestamp?: number) =>
ipcRenderer.invoke('chat:searchMessages', keyword, sessionId, limit, offset, beginTimestamp, endTimestamp),
getMyFootprintStats: (
beginTimestamp: number,
endTimestamp: number,
options?: {
myAccountId?: string
privateSessionIds?: string[]
groupSessionIds?: string[]
mentionLimit?: number
privateLimit?: number
mentionMode?: 'text_at_me' | string
}
) => ipcRenderer.invoke('chat:getMyFootprintStats', beginTimestamp, endTimestamp, options),
exportMyFootprint: (
beginTimestamp: number,
endTimestamp: number,
format: 'csv' | 'json',
filePath: string
) => ipcRenderer.invoke('chat:exportMyFootprint', beginTimestamp, endTimestamp, format, filePath),
onWcdbChange: (callback: (event: any, data: { type: string; json: string }) => void) => {
ipcRenderer.on('wcdb-change', callback)
return () => ipcRenderer.removeListener('wcdb-change', callback)
}
},
// 图片解密
image: {
decrypt: (payload: {
sessionId?: string
imageMd5?: string
imageDatName?: string
createTime?: number
force?: boolean
preferFilePath?: boolean
hardlinkOnly?: boolean
disableUpdateCheck?: boolean
allowCacheIndex?: boolean
allowFilesystemScan?: boolean
suppressEvents?: boolean
}) =>
ipcRenderer.invoke('image:decrypt', payload),
resolveCache: (payload: {
sessionId?: string
imageMd5?: string
imageDatName?: string
createTime?: number
preferFilePath?: boolean
hardlinkOnly?: boolean
disableUpdateCheck?: boolean
allowCacheIndex?: boolean
allowCachePromotion?: boolean
allowFilesystemScan?: boolean
suppressEvents?: boolean
}) =>
ipcRenderer.invoke('image:resolveCache', payload),
resolveCacheBatch: (
payloads: Array<{ sessionId?: string; imageMd5?: string; imageDatName?: string; createTime?: number; preferFilePath?: boolean; hardlinkOnly?: boolean }>,
options?: { disableUpdateCheck?: boolean; allowCacheIndex?: boolean; allowCachePromotion?: boolean; allowFilesystemScan?: boolean; preferFilePath?: boolean; hardlinkOnly?: boolean; suppressEvents?: boolean }
) => ipcRenderer.invoke('image:resolveCacheBatch', payloads, options),
preload: (
payloads: Array<{ sessionId?: string; imageMd5?: string; imageDatName?: string; createTime?: number }>,
options?: { allowDecrypt?: boolean; allowCacheIndex?: boolean; allowFilesystemScan?: boolean; emitResolved?: boolean; scope?: string; priority?: 'high' | 'normal' | 'low' }
) => ipcRenderer.invoke('image:preload', payloads, options),
cancelPreloadScope: (scope: string) =>
ipcRenderer.invoke('image:cancelPreloadScope', scope),
getPreloadStats: () =>
ipcRenderer.invoke('image:getPreloadStats'),
preloadHardlinkMd5s: (md5List: string[], options?: { chunkSize?: number; yieldMs?: number; filesystemFallback?: boolean }) =>
ipcRenderer.invoke('image:preloadHardlinkMd5s', md5List, options),
onUpdateAvailable: (callback: (payload: { cacheKey: string; sessionId?: string; createTime?: number; imageMd5?: string; imageDatName?: string }) => void) => {
const listener = (_: unknown, payload: { cacheKey: string; sessionId?: string; createTime?: number; imageMd5?: string; imageDatName?: string }) => callback(payload)
ipcRenderer.on('image:updateAvailable', listener)
return () => ipcRenderer.removeListener('image:updateAvailable', listener)
},
onCacheResolved: (callback: (payload: { cacheKey: string; sessionId?: string; createTime?: number; imageMd5?: string; imageDatName?: string; localPath: string }) => void) => {
const listener = (_: unknown, payload: { cacheKey: string; sessionId?: string; createTime?: number; imageMd5?: string; imageDatName?: string; localPath: string }) => callback(payload)
ipcRenderer.on('image:cacheResolved', listener)
return () => ipcRenderer.removeListener('image:cacheResolved', listener)
},
onDecryptProgress: (callback: (payload: {
cacheKey: string
imageMd5?: string
imageDatName?: string
stage: 'queued' | 'locating' | 'decrypting' | 'writing' | 'done' | 'failed'
progress: number
status: 'running' | 'done' | 'error'
message?: string
}) => void) => {
const listener = (_: unknown, payload: {
cacheKey: string
imageMd5?: string
imageDatName?: string
stage: 'queued' | 'locating' | 'decrypting' | 'writing' | 'done' | 'failed'
progress: number
status: 'running' | 'done' | 'error'
message?: string
}) => callback(payload)
ipcRenderer.on('image:decryptProgress', listener)
return () => ipcRenderer.removeListener('image:decryptProgress', listener)
},
startAutoDownload: (whitelist: string[] | string) => ipcRenderer.invoke('image:startAutoDownload', whitelist),
stopAutoDownload: () => ipcRenderer.invoke('image:stopAutoDownload'),
getAutoDownloadStatus: () => ipcRenderer.invoke('image:getAutoDownloadStatus')
},
// 视频
video: {
getVideoInfo: (videoMd5: string, options?: { includePoster?: boolean; posterFormat?: 'dataUrl' | 'fileUrl' }) => ipcRenderer.invoke('video:getVideoInfo', videoMd5, options),
getVideoInfoBatch: (videoMd5List: string[], options?: { includePoster?: boolean; posterFormat?: 'dataUrl' | 'fileUrl' }) =>
ipcRenderer.invoke('video:getVideoInfoBatch', videoMd5List, options),
parseVideoMd5: (content: string) => ipcRenderer.invoke('video:parseVideoMd5', content)
},
process: {
platform: process.platform,
arch: process.arch
},
// 数据分析
analytics: {
getOverallStatistics: (force?: boolean) => ipcRenderer.invoke('analytics:getOverallStatistics', force),
getContactRankings: (limit?: number, beginTimestamp?: number, endTimestamp?: number) =>
ipcRenderer.invoke('analytics:getContactRankings', limit, beginTimestamp, endTimestamp),
getTimeDistribution: () => ipcRenderer.invoke('analytics:getTimeDistribution'),
getSelfSentDailyDistribution: (beginTimestamp?: number, endTimestamp?: number, force?: boolean) =>
ipcRenderer.invoke('analytics:getSelfSentDailyDistribution', beginTimestamp, endTimestamp, force),
getExcludedUsernames: () => ipcRenderer.invoke('analytics:getExcludedUsernames'),
setExcludedUsernames: (usernames: string[]) => ipcRenderer.invoke('analytics:setExcludedUsernames', usernames),
getExcludeCandidates: () => ipcRenderer.invoke('analytics:getExcludeCandidates'),
onProgress: (callback: (payload: { status: string; progress: number }) => void) => {
ipcRenderer.on('analytics:progress', (_, payload) => callback(payload))
return () => ipcRenderer.removeAllListeners('analytics:progress')
}
},
// 缓存管理
cache: {
clearAnalytics: () => ipcRenderer.invoke('cache:clearAnalytics'),
clearImages: () => ipcRenderer.invoke('cache:clearImages'),
clearAll: () => ipcRenderer.invoke('cache:clearAll')
},
// 群聊分析
groupAnalytics: {
getGroupChats: () => ipcRenderer.invoke('groupAnalytics:getGroupChats'),
getGroupMembers: (chatroomId: string) => ipcRenderer.invoke('groupAnalytics:getGroupMembers', chatroomId),
getGroupMembersPanelData: (
chatroomId: string,
options?: { forceRefresh?: boolean; includeMessageCounts?: boolean }
) => ipcRenderer.invoke('groupAnalytics:getGroupMembersPanelData', chatroomId, options),
getGroupMessageRanking: (chatroomId: string, limit?: number, startTime?: number, endTime?: number) => ipcRenderer.invoke('groupAnalytics:getGroupMessageRanking', chatroomId, limit, startTime, endTime),
getGroupActiveHours: (chatroomId: string, startTime?: number, endTime?: number) => ipcRenderer.invoke('groupAnalytics:getGroupActiveHours', chatroomId, startTime, endTime),
getGroupMediaStats: (chatroomId: string, startTime?: number, endTime?: number) => ipcRenderer.invoke('groupAnalytics:getGroupMediaStats', chatroomId, startTime, endTime),
getGroupMemberAnalytics: (chatroomId: string, memberUsername: string, startTime?: number, endTime?: number) => ipcRenderer.invoke('groupAnalytics:getGroupMemberAnalytics', chatroomId, memberUsername, startTime, endTime),
getGroupMemberMessages: (
chatroomId: string,
memberUsername: string,
options?: { startTime?: number; endTime?: number; limit?: number; cursor?: number }
) => ipcRenderer.invoke('groupAnalytics:getGroupMemberMessages', chatroomId, memberUsername, options),
exportGroupMembers: (chatroomId: string, outputPath: string) => ipcRenderer.invoke('groupAnalytics:exportGroupMembers', chatroomId, outputPath),
exportGroupMemberMessages: (chatroomId: string, memberUsername: string, outputPath: string, startTime?: number, endTime?: number) =>
ipcRenderer.invoke('groupAnalytics:exportGroupMemberMessages', chatroomId, memberUsername, outputPath, startTime, endTime)
},
// 年度报告
annualReport: {
getAvailableYears: () => ipcRenderer.invoke('annualReport:getAvailableYears'),
startAvailableYearsLoad: () => ipcRenderer.invoke('annualReport:startAvailableYearsLoad'),
cancelAvailableYearsLoad: (taskId: string) => ipcRenderer.invoke('annualReport:cancelAvailableYearsLoad', taskId),
generateReport: (year: number) => ipcRenderer.invoke('annualReport:generateReport', year),
exportImages: (payload: { baseDir: string; folderName: string; images: Array<{ name: string; dataUrl: string }> }) =>
ipcRenderer.invoke('annualReport:exportImages', payload),
captureCurrentWindow: () => ipcRenderer.invoke('annualReport:captureCurrentWindow'),
onAvailableYearsProgress: (callback: (payload: {
taskId: string
years?: number[]
done: boolean
error?: string
canceled?: boolean
strategy?: 'cache' | 'native' | 'hybrid'
phase?: 'cache' | 'native' | 'scan' | 'done'
statusText?: string
nativeElapsedMs?: number
scanElapsedMs?: number
totalElapsedMs?: number
switched?: boolean
nativeTimedOut?: boolean
}) => void) => {
ipcRenderer.on('annualReport:availableYearsProgress', (_, payload) => callback(payload))
return () => ipcRenderer.removeAllListeners('annualReport:availableYearsProgress')
},
onProgress: (callback: (payload: { status: string; progress: number }) => void) => {
ipcRenderer.on('annualReport:progress', (_, payload) => callback(payload))
return () => ipcRenderer.removeAllListeners('annualReport:progress')
}
},
dualReport: {
generateReport: (payload: { friendUsername: string; year: number }) =>
ipcRenderer.invoke('dualReport:generateReport', payload),
onProgress: (callback: (payload: { status: string; progress: number }) => void) => {
ipcRenderer.on('dualReport:progress', (_, payload) => callback(payload))
return () => ipcRenderer.removeAllListeners('dualReport:progress')
}
},
// 导出
export: {
getExportStats: (sessionIds: string[], options: any) =>
ipcRenderer.invoke('export:getExportStats', sessionIds, options),
exportSessions: (sessionIds: string[], outputDir: string, options: any, controlOptions?: { taskId?: string }) =>
ipcRenderer.invoke('export:exportSessions', sessionIds, outputDir, options, controlOptions),
pauseTask: (taskId: string) =>
ipcRenderer.invoke('export:pauseTask', taskId),
resumeTask: (taskId: string) =>
ipcRenderer.invoke('export:resumeTask', taskId),
cancelTask: (taskId: string) =>
ipcRenderer.invoke('export:cancelTask', taskId),
exportSession: (sessionId: string, outputPath: string, options: any) =>
ipcRenderer.invoke('export:exportSession', sessionId, outputPath, options),
exportContacts: (outputDir: string, options: any) =>
ipcRenderer.invoke('export:exportContacts', outputDir, options),
onProgress: (callback: (payload: {
current: number
total: number
currentSession: string
currentSessionId?: string
phase: string
phaseProgress?: number
phaseTotal?: number
phaseLabel?: string
collectedMessages?: number
exportedMessages?: number
estimatedTotalMessages?: number
writtenFiles?: number
}) => void) => {
ipcRenderer.on('export:progress', (_, payload) => callback(payload))
return () => ipcRenderer.removeAllListeners('export:progress')
}
},
whisper: {
downloadModel: () =>
ipcRenderer.invoke('whisper:downloadModel'),
getModelStatus: () =>
ipcRenderer.invoke('whisper:getModelStatus'),
onDownloadProgress: (callback: (payload: { modelName: string; downloadedBytes: number; totalBytes?: number; percent?: number }) => void) => {
ipcRenderer.on('whisper:downloadProgress', (_, payload) => callback(payload))
return () => ipcRenderer.removeAllListeners('whisper:downloadProgress')
}
},
// 朋友圈
sns: {
getTimeline: (limit: number, offset: number, usernames?: string[], keyword?: string, startTime?: number, endTime?: number) =>
ipcRenderer.invoke('sns:getTimeline', limit, offset, usernames, keyword, startTime, endTime),
getSnsUsernames: () => ipcRenderer.invoke('sns:getSnsUsernames'),
getUserPostCounts: (options?: { preferCache?: boolean; forceRefresh?: boolean }) => ipcRenderer.invoke('sns:getUserPostCounts', options),
getExportStatsFast: () => ipcRenderer.invoke('sns:getExportStatsFast'),
getExportStats: (options?: { allowTimelineFallback?: boolean; preferCache?: boolean; forceRefresh?: boolean }) =>
ipcRenderer.invoke('sns:getExportStats', options),
getUserPostStats: (username: string) => ipcRenderer.invoke('sns:getUserPostStats', username),
debugResource: (url: string) => ipcRenderer.invoke('sns:debugResource', url),
proxyImage: (payload: { url: string; key?: string | number }) => ipcRenderer.invoke('sns:proxyImage', payload),
downloadImage: (payload: { url: string; key?: string | number }) => ipcRenderer.invoke('sns:downloadImage', payload),
exportTimeline: (options: any) => ipcRenderer.invoke('sns:exportTimeline', options),
onExportProgress: (callback: (payload: any) => void) => {
ipcRenderer.on('sns:exportProgress', (_, payload) => callback(payload))
return () => ipcRenderer.removeAllListeners('sns:exportProgress')
},
selectExportDir: () => ipcRenderer.invoke('sns:selectExportDir'),
installBlockDeleteTrigger: () => ipcRenderer.invoke('sns:installBlockDeleteTrigger'),
uninstallBlockDeleteTrigger: () => ipcRenderer.invoke('sns:uninstallBlockDeleteTrigger'),
checkBlockDeleteTrigger: () => ipcRenderer.invoke('sns:checkBlockDeleteTrigger'),
deleteSnsPost: (postId: string) => ipcRenderer.invoke('sns:deleteSnsPost', postId),
downloadEmoji: (params: { url: string; encryptUrl?: string; aesKey?: string }) => ipcRenderer.invoke('sns:downloadEmoji', params),
getCacheMigrationStatus: () => ipcRenderer.invoke('sns:getCacheMigrationStatus'),
startCacheMigration: () => ipcRenderer.invoke('sns:startCacheMigration'),
onCacheMigrationProgress: (callback: (payload: any) => void) => {
const listener = (_event: unknown, payload: any) => callback(payload)
ipcRenderer.on('sns:cacheMigrationProgress', listener)
return () => ipcRenderer.removeListener('sns:cacheMigrationProgress', listener)
}
},
biz: {
listAccounts: (account?: string) => ipcRenderer.invoke('biz:listAccounts', account),
listAccountHealth: (account?: string) => ipcRenderer.invoke('biz:listAccountHealth', account),
listMessages: (username: string, account?: string, limit?: number, offset?: number) =>
ipcRenderer.invoke('biz:listMessages', username, account, limit, offset),
listPayRecords: (account?: string, limit?: number, offset?: number) =>
ipcRenderer.invoke('biz:listPayRecords', account, limit, offset)
},
// 数据收集
cloud: {
init: () => ipcRenderer.invoke('cloud:init'),
recordPage: (pageName: string) => ipcRenderer.invoke('cloud:recordPage', pageName),
getLogs: () => ipcRenderer.invoke('cloud:getLogs')
},
// HTTP API 服务
http: {
start: (port?: number, host?: string) => ipcRenderer.invoke('http:start', port, host),
stop: () => ipcRenderer.invoke('http:stop'),
status: () => ipcRenderer.invoke('http:status')
},
// AI 见解
insight: {
testConnection: (override?: unknown) => ipcRenderer.invoke('insight:testConnection', override),
getTodayStats: () => ipcRenderer.invoke('insight:getTodayStats'),
listRecords: (filters?: any) => ipcRenderer.invoke('insight:listRecords', filters),
getRecord: (id: string) => ipcRenderer.invoke('insight:getRecord', id),
markRecordRead: (id: string) => ipcRenderer.invoke('insight:markRecordRead', id),
clearRecords: (filters?: any) => ipcRenderer.invoke('insight:clearRecords', filters),
triggerTest: () => ipcRenderer.invoke('insight:triggerTest'),
triggerSessionInsight: (payload: {
sessionId: string
displayName?: string
avatarUrl?: string
}) => ipcRenderer.invoke('insight:triggerSessionInsight', payload),
listProfileStatuses: (sessionIds: string[]) => ipcRenderer.invoke('insight:listProfileStatuses', sessionIds),
generateProfile: (payload: {
sessionId: string
displayName?: string
avatarUrl?: string
}) => ipcRenderer.invoke('insight:generateProfile', payload),
cancelProfile: (sessionId?: string) => ipcRenderer.invoke('insight:cancelProfile', sessionId),
generateFootprintInsight: (payload: {
rangeLabel: string
summary: {
private_inbound_people?: number
private_replied_people?: number
private_outbound_people?: number
private_reply_rate?: number
mention_count?: number
mention_group_count?: number
}
privateSegments?: Array<{ displayName?: string; session_id?: string; incoming_count?: number; outgoing_count?: number; message_count?: number; replied?: boolean }>
mentionGroups?: Array<{ displayName?: string; session_id?: string; count?: number }>
}) => ipcRenderer.invoke('insight:generateFootprintInsight', payload),
generateMessageInsight: (payload: {
sessionId: string
displayName?: string
avatarUrl?: string
targetLocalId?: number
targetCreateTime?: number
targetMessageKey?: string
targetText: string
targetSenderName?: string
contextCount?: number
forceRefresh?: boolean
}) => ipcRenderer.invoke('insight:generateMessageInsight', payload)
},
ai: {
getProviders: () => ipcRenderer.invoke('ai:getProviders'),
resolveModelMetadata: (options: unknown) => ipcRenderer.invoke('ai:resolveModelMetadata', options),
listModels: (options: unknown) => ipcRenderer.invoke('ai:listModels', options)
},
relayOne: {
getStatus: () => ipcRenderer.invoke('relayone:getStatus'),
sendVerificationCode: (email: string) => ipcRenderer.invoke('relayone:sendVerificationCode', email),
register: (input: unknown) => ipcRenderer.invoke('relayone:register', input),
login: (input: unknown) => ipcRenderer.invoke('relayone:login', input),
login2FA: (input: unknown) => ipcRenderer.invoke('relayone:login2FA', input),
logout: () => ipcRenderer.invoke('relayone:logout'),
getAccount: () => ipcRenderer.invoke('relayone:getAccount'),
listApiKeys: () => ipcRenderer.invoke('relayone:listApiKeys'),
listGroups: () => ipcRenderer.invoke('relayone:listGroups'),
createApiKey: (input: unknown) => ipcRenderer.invoke('relayone:createApiKey', input),
updateApiKeyGroup: (input: unknown) => ipcRenderer.invoke('relayone:updateApiKeyGroup', input),
deleteApiKey: (keyId: number) => ipcRenderer.invoke('relayone:deleteApiKey', keyId),
getCheckoutInfo: () => ipcRenderer.invoke('relayone:getCheckoutInfo'),
createOrder: (input: unknown) => ipcRenderer.invoke('relayone:createOrder', input),
getOrder: (orderId: number) => ipcRenderer.invoke('relayone:getOrder', orderId)
},
agent: {
getDebugLogInfo: () => ipcRenderer.invoke('agent:getDebugLogInfo'),
openDebugLogDirectory: () => ipcRenderer.invoke('agent:openDebugLogDirectory'),
run: (runId: string, messages: unknown[], scope?: unknown, modelConfig?: unknown, conversationId?: number | null, mode?: unknown, resumeFromRunId?: string) =>
ipcRenderer.invoke('agent:run', { runId, messages, scope, modelConfig, conversationId, mode, resumeFromRunId }),
abort: (runId: string) => ipcRenderer.invoke('agent:abort', runId),
listRunStates: (limit?: number) => ipcRenderer.invoke('agent:listRunStates', limit),
getConversationRunState: (conversationId: number) => ipcRenderer.invoke('agent:getConversationRunState', conversationId),
loadRunState: (runId: string, includeEvidenceResults?: boolean) => ipcRenderer.invoke('agent:loadRunState', runId, includeEvidenceResults),
replayRunAnswer: (runId: string, modelConfig?: unknown) => ipcRenderer.invoke('agent:replayRunAnswer', runId, modelConfig),
listFeedback: (limit?: number) => ipcRenderer.invoke('agent:listFeedback', limit),
saveFeedback: (payload: unknown) => ipcRenderer.invoke('agent:saveFeedback', payload),
deleteFeedback: (messageId: string, modelConfig?: unknown) => ipcRenderer.invoke('agent:deleteFeedback', messageId, modelConfig),
saveImageAttachment: (payload: unknown) => ipcRenderer.invoke('agent:saveImageAttachment', payload),
generateTitle: (firstMessage: string, modelConfig?: unknown) => ipcRenderer.invoke('agent:generateTitle', { firstMessage, modelConfig }),
replySuggest: (input: unknown, modelConfig?: unknown) => ipcRenderer.invoke('agent:replySuggest', { input, modelConfig }),
listConversations: (scope?: unknown) => ipcRenderer.invoke('agent:listConversations', scope),
loadConversation: (id: number) => ipcRenderer.invoke('agent:loadConversation', id),
createConversation: (payload: unknown) => ipcRenderer.invoke('agent:createConversation', payload),
deleteConversation: (idOrPayload: unknown) => ipcRenderer.invoke('agent:deleteConversation', idOrPayload),
deleteConversationsByScope: (scope?: unknown) => ipcRenderer.invoke('agent:deleteConversationsByScope', scope),
renameConversation: (id: number, title: string, originClientId?: string) => ipcRenderer.invoke('agent:renameConversation', id, title, originClientId),
updateConversationMetadata: (id: number, patch: unknown, originClientId?: string) => ipcRenderer.invoke('agent:updateConversationMetadata', id, patch, originClientId),
saveConversationMessages: (payload: unknown) => ipcRenderer.invoke('agent:saveConversationMessages', payload),
getLastConversation: (scope?: unknown) => ipcRenderer.invoke('agent:getLastConversation', scope),
listMemories: (options?: unknown) => ipcRenderer.invoke('agent:listMemories', options),
getMemorySummary: () => ipcRenderer.invoke('agent:getMemorySummary'),
updateMemorySummary: (instruction: string, modelConfig?: unknown, requestId?: string) => ipcRenderer.invoke('agent:updateMemorySummary', { instruction, modelConfig, requestId }),
refreshMemorySummary: (modelConfig?: unknown, requestId?: string) => ipcRenderer.invoke('agent:refreshMemorySummary', { modelConfig, requestId }),
setMemoryEnabled: (enabled: boolean) => ipcRenderer.invoke('agent:setMemoryEnabled', enabled),
clearMemorySummary: (options?: unknown) => ipcRenderer.invoke('agent:clearMemorySummary', options),
createMemory: (payload: unknown) => ipcRenderer.invoke('agent:createMemory', payload),
updateMemory: (id: string, patch: unknown) => ipcRenderer.invoke('agent:updateMemory', id, patch),
deleteMemory: (id: string) => ipcRenderer.invoke('agent:deleteMemory', id),
onMemoryUpdated: (callback: (summary: unknown) => void) => {
const listener = (_: unknown, summary: unknown) => callback(summary)
ipcRenderer.on('agent:memoryUpdated', listener)
return () => ipcRenderer.removeListener('agent:memoryUpdated', listener)
},
onMemoryPreview: (callback: (preview: unknown) => void) => {
const listener = (_: unknown, preview: unknown) => callback(preview)
ipcRenderer.on('agent:memoryPreview', listener)
return () => ipcRenderer.removeListener('agent:memoryPreview', listener)
},
onConversationUpdated: (callback: (event: unknown) => void) => {
const listener = (_: unknown, event: unknown) => callback(event)
ipcRenderer.on('agent:conversationUpdated', listener)
return () => ipcRenderer.removeListener('agent:conversationUpdated', listener)
},
onChunk: (runId: string, callback: (chunk: unknown) => void) => {
const listener = (_: unknown, payload: { runId: string; chunk: unknown }) => {
if (payload?.runId === runId) callback(payload.chunk)
}
ipcRenderer.on('agent:chunk', listener)
return () => ipcRenderer.removeListener('agent:chunk', listener)
},
onProgress: (runId: string, callback: (progress: unknown) => void) => {
const listener = (_: unknown, payload: { runId: string; progress: unknown }) => {
if (payload?.runId === runId) callback(payload.progress)
}
ipcRenderer.on('agent:progress', listener)
return () => ipcRenderer.removeListener('agent:progress', listener)
},
},
file: { writeBase64: (filePath: string, base64: string) => ipcRenderer.invoke('file:writeBase64', filePath, base64) },
tts: { speak: async () => ({ success: false, error: '未配置语音服务' }) },
groupSummary: {
listRecords: (filters?: any) => ipcRenderer.invoke('groupSummary:listRecords', filters),
getRecord: (id: string) => ipcRenderer.invoke('groupSummary:getRecord', id),
triggerManual: (payload: {
sessionId: string
displayName?: string
avatarUrl?: string
startTime: number
endTime: number
}) => ipcRenderer.invoke('groupSummary:triggerManual', payload),
triggerDay: (payload: {
sessionId: string
displayName?: string
avatarUrl?: string
date: string
}) => ipcRenderer.invoke('groupSummary:triggerDay', payload)
},
social: {
saveWeiboCookie: (rawInput: string) => ipcRenderer.invoke('social:saveWeiboCookie', rawInput),
validateWeiboUid: (uid: string) => ipcRenderer.invoke('social:validateWeiboUid', uid)
}
})