d3ro-voice/apps/desktop/src/preload/index.ts
Yun Chan 0fbbbc1756
Some checks failed
deploy-site / deploy (push) Failing after 1m15s
fix(release): restore automatic updates by shipping the speech engine on demand
Auto-update could not work at all: the installer was 189 MB because it carried
the local speech engine and ffmpeg, and the download feed rejects uploads over
about 100 MiB, so update metadata could never be published.

The installer now leaves those components out and the app fetches them the first
time they are needed, verifying every part and the joined archive before
installing. The installer is 90.6 MiB, the update feed is published again, and
updates stay small because the engine is not re-sent on every release.

The fetch is visible and recoverable: the download runs with progress, a failed
install cleans up after itself, and Settings > STT shows the runtime status with
a manual download action for when the automatic one cannot run.
2026-09-18 13:51:49 +09:00

891 lines
42 KiB
TypeScript

// src/preload/index.ts
// contextBridge로 렌더러에 노출할 API 정의
import { contextBridge, ipcRenderer } from 'electron'
import { IPC_CHANNELS } from '@d3ro/core/ipc-channels'
import type {
AudioDevice,
SetDeviceParams,
TestDeviceParams,
TestDeviceResult,
AudioDeviceChangedEvent,
AppConfig,
ConfigGetParams,
ConfigSetParams,
ConfigResetParams,
SetThemeParams,
SetLanguageParams,
ThemeMode,
ConfigChangedEvent,
VoiceState,
VoiceMode,
AudioLevelEvent,
VoiceStateChangedEvent,
TranscriptionDeltaEvent,
TranscriptionCompleteEvent,
VoiceErrorEvent,
StartRecordingParams,
StartRecordingResult,
StopRecordingParams,
StopRecordingResult,
CancelRecordingParams,
SetVoiceModeParams,
STTStatus,
STTModel,
SetSTTModelParams,
SetSTTLanguageParams,
STTStatusChangedEvent,
DownloadModelParams,
DownloadProgressEvent,
STTProviderType,
STTProviderInfo,
STTProviderConfig,
SetSTTProviderParams,
SetSTTProviderConfigParams,
TestSTTConnectionParams,
TestSTTConnectionResult,
HotkeyBinding,
SetHotkeyParams,
SetEnabledParams,
HotkeyTriggeredEvent,
LLMStatus,
LLMModel,
LLMProcessParams,
LLMProcessResult,
SetLLMModelParams,
SetServerUrlParams,
LLMStatusChangedEvent,
LLMProcessProgressEvent,
HistoryQueryParams,
HistoryPage,
HistoryGetByIdParams,
HistoryEntry,
HistoryDeleteParams,
HistorySearchParams,
DictionaryQueryParams,
DictionaryPage,
DictionaryEntry,
DictionaryAddParams,
DictionaryUpdateParams,
DictionaryDeleteParams,
DictionarySearchParams,
DictionaryImportParams,
DictionaryImportResult,
DictionaryExportParams,
StatsSummary,
PermissionStatus,
// Phase 10
MemoTag,
AddTagParams,
RemoveTagParams,
GetTagsParams,
SearchByTagParams,
ExportMemoParams,
TagCount,
VoiceCommandRule,
VoiceCommandMatch,
SetVoiceCommandKeywordsParams,
SetVoiceCommandEnabledParams,
CaptureContextResult,
LLMChain,
CreateChainParams,
UpdateChainParams,
DeleteChainParams,
ExecuteChainParams,
ChainExecutionResult,
ChainProgress,
CaptionState,
CaptionSegment,
CaptionConfig,
// Phase 11
LicenseInfo,
ActivateLicenseParams,
ActivateLicenseResult,
FeatureAccess,
UsageQuota,
UpgradePromptEvent,
TierComparison,
// Phase 12
FileTranscriptionStartParams,
// Phase 13.1
ConversationSessionInfo,
ConversationMessage,
ConversationSendParams,
ConversationAssistantDelta,
ConversationAssistantMessage,
ConversationError,
RealtimeTokenParams,
RealtimeTokenResult,
// Phase 13.2
RAGDocument,
RAGQueryParams,
RAGQueryResult,
RAGStateInfo,
RAGIndexProgress,
RAGAddDocumentParams,
RAGRemoveDocumentParams,
// Phase 13.3
VoiceActionExecuteParams,
VoiceActionPreset,
VoiceActionHistoryEntry,
VoiceActionPlannedEvent,
VoiceActionExecutedEvent,
VoiceActionErrorEvent,
FileTranscriptionProgress,
FileTranscriptionResult,
FileTranscriptionStateInfo,
MeetingSummarizeParams,
MeetingSummaryResult,
MeetingSummaryGetParams,
MeetingSummaryExportParams,
MeetingSummaryProgress,
DictationTemplate,
CreateTemplateParams,
UpdateTemplateParams,
DeleteTemplateParams,
StartTemplateSessionParams,
SetFieldValueParams,
TemplateSessionInfo,
TemplateFieldCompletedEvent,
TemplateSessionCompletedEvent,
MeetingStartResult,
MeetingMemo,
MeetingAddMemoParams,
MeetingModeStateInfo,
MeetingSessionPage,
MeetingGetSessionsParams,
MeetingSessionDetail,
MeetingGetSessionParams,
MeetingDeleteSessionParams,
MeetingExportParams,
MeetingProcessingProgress,
MeetingDocument,
MeetingDocTemplate,
MeetingGenerateDocParams,
MeetingUpdateDocParams,
MeetingDeleteDocParams,
MeetingGetDocsParams,
MeetingUpdateTranscriptParams,
MeetingExportDocParams,
MeetingExportTranscriptParams,
MeetingDocGeneratingProgress,
CreateMeetingDocTemplateParams,
UpdateMeetingDocTemplateParams,
DeleteMeetingDocTemplateParams,
MeetingChatMessage,
MeetingChatSendParams,
MeetingChatDelta,
DiarizeSessionParams,
} from '@d3ro/core/types'
import { Feature } from '@d3ro/core/types'
import type { IPCResult } from '@d3ro/core/errors'
/** 로컬 AI 런타임(사이드카 엔진/ffmpeg) 상태 — RuntimeProvisioner가 제공한다 */
type RuntimeComponentName = 'sidecar' | 'ffmpeg'
interface RuntimeStatusPayload {
component: RuntimeComponentName
installed: boolean
path: string
sizeBytes: number
}
interface RuntimeProgressPayload {
component: RuntimeComponentName
phase: 'index' | 'downloading' | 'extracting' | 'done'
percent: number
downloadedBytes: number
totalBytes: number
bytesPerSecond: number
}
type Unsubscribe = () => void
function invoke<TResult>(channel: string, ...args: unknown[]): Promise<IPCResult<TResult>> {
return ipcRenderer.invoke(channel, ...args)
}
function send(channel: string, ...args: unknown[]): void {
ipcRenderer.send(channel, ...args)
}
function on<T>(channel: string, callback: (data: T) => void): Unsubscribe {
const listener = (_event: Electron.IpcRendererEvent, data: T) => callback(data)
ipcRenderer.on(channel, listener)
return () => ipcRenderer.removeListener(channel, listener)
}
const electronAPI = {
// ── Platform ───────────────────────────────────────────
// 'darwin' | 'win32' | 'linux' — 렌더러에서 키바인딩/단축키 표기 분기
platform: process.platform as NodeJS.Platform,
// ── Audio ──────────────────────────────────────────────
audio: {
getDevices: () => invoke<AudioDevice[]>(IPC_CHANNELS.AUDIO.GET_DEVICES),
getSelectedDevice: () => invoke<string | null>(IPC_CHANNELS.AUDIO.GET_SELECTED_DEVICE),
setSelectedDevice: (params: SetDeviceParams) =>
invoke<void>(IPC_CHANNELS.AUDIO.SET_SELECTED_DEVICE, params),
testDevice: (params: TestDeviceParams) =>
invoke<TestDeviceResult>(IPC_CHANNELS.AUDIO.TEST_DEVICE, params),
stopTest: () => invoke<void>(IPC_CHANNELS.AUDIO.STOP_TEST),
onDeviceChanged: (cb: (e: AudioDeviceChangedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.AUDIO.DEVICE_CHANGED, cb),
onTestLevel: (cb: (e: { level: number; done: boolean }) => void): Unsubscribe =>
on(IPC_CHANNELS.AUDIO.TEST_LEVEL, cb),
},
// ── Config ─────────────────────────────────────────────
config: {
get: (params: ConfigGetParams) => invoke<unknown>(IPC_CHANNELS.CONFIG.GET, params),
set: (params: ConfigSetParams) => invoke<void>(IPC_CHANNELS.CONFIG.SET, params),
getAll: () => invoke<AppConfig>(IPC_CHANNELS.CONFIG.GET_ALL),
reset: (params: ConfigResetParams) => invoke<void>(IPC_CHANNELS.CONFIG.RESET, params),
getTheme: () => invoke<ThemeMode>(IPC_CHANNELS.CONFIG.GET_THEME),
setTheme: (params: SetThemeParams) => invoke<void>(IPC_CHANNELS.CONFIG.SET_THEME, params),
getLanguage: () => invoke<string>(IPC_CHANNELS.CONFIG.GET_LANGUAGE),
setLanguage: (params: SetLanguageParams) =>
invoke<void>(IPC_CHANNELS.CONFIG.SET_LANGUAGE, params),
onChanged: (cb: (e: ConfigChangedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.CONFIG.CHANGED, cb)
},
// ── Voice ──────────────────────────────────────────────
voice: {
startRecording: (params: StartRecordingParams) =>
invoke<StartRecordingResult>(IPC_CHANNELS.VOICE.START_RECORDING, params),
stopRecording: (params: StopRecordingParams) =>
invoke<StopRecordingResult>(IPC_CHANNELS.VOICE.STOP_RECORDING, params),
cancelRecording: (params: CancelRecordingParams) =>
invoke<void>(IPC_CHANNELS.VOICE.CANCEL_RECORDING, params),
getState: () => invoke<VoiceState>(IPC_CHANNELS.VOICE.GET_STATE),
setMode: (params: SetVoiceModeParams) =>
invoke<void>(IPC_CHANNELS.VOICE.SET_MODE, params),
getMode: () => invoke<VoiceMode>(IPC_CHANNELS.VOICE.GET_MODE),
onStateChanged: (cb: (e: VoiceStateChangedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE.STATE_CHANGED, cb),
onTranscriptionDelta: (cb: (e: TranscriptionDeltaEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE.TRANSCRIPTION_DELTA, cb),
onTranscriptionComplete: (cb: (e: TranscriptionCompleteEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE.TRANSCRIPTION_COMPLETE, cb),
onError: (cb: (e: VoiceErrorEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE.ERROR, cb),
onAudioLevel: (cb: (e: AudioLevelEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE.AUDIO_LEVEL, cb)
},
// ── STT ────────────────────────────────────────────────
stt: {
getStatus: () => invoke<STTStatus>(IPC_CHANNELS.STT.GET_STATUS),
getModels: () => invoke<STTModel[]>(IPC_CHANNELS.STT.GET_MODELS),
getActiveModel: () => invoke<string | null>(IPC_CHANNELS.STT.GET_ACTIVE_MODEL),
setModel: (params: SetSTTModelParams) =>
invoke<void>(IPC_CHANNELS.STT.SET_MODEL, params),
downloadModel: (params: DownloadModelParams) =>
invoke<void>(IPC_CHANNELS.STT.DOWNLOAD_MODEL, params),
cancelDownload: () => invoke<void>(IPC_CHANNELS.STT.CANCEL_DOWNLOAD),
getLanguage: () => invoke<string>(IPC_CHANNELS.STT.GET_LANGUAGE),
setLanguage: (params: SetSTTLanguageParams) =>
invoke<void>(IPC_CHANNELS.STT.SET_LANGUAGE, params),
getProviders: () => invoke<STTProviderInfo[]>(IPC_CHANNELS.STT.GET_PROVIDERS),
getActiveProvider: () => invoke<STTProviderType>(IPC_CHANNELS.STT.GET_ACTIVE_PROVIDER),
setProvider: (params: SetSTTProviderParams) =>
invoke<void>(IPC_CHANNELS.STT.SET_PROVIDER, params),
getProviderConfig: (params: { provider: STTProviderType }) =>
invoke<STTProviderConfig>(IPC_CHANNELS.STT.GET_PROVIDER_CONFIG, params),
setProviderConfig: (params: SetSTTProviderConfigParams) =>
invoke<void>(IPC_CHANNELS.STT.SET_PROVIDER_CONFIG, params),
testConnection: (params: TestSTTConnectionParams) =>
invoke<TestSTTConnectionResult>(IPC_CHANNELS.STT.TEST_CONNECTION, params),
onStatusChanged: (cb: (e: STTStatusChangedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.STT.STATUS_CHANGED, cb),
onDownloadProgress: (cb: (e: DownloadProgressEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.STT.DOWNLOAD_PROGRESS, cb)
},
// ── Local AI runtime (엔진/ffmpeg — 필요할 때 내려받음) ──
runtime: {
getStatus: () => invoke<RuntimeStatusPayload[]>(IPC_CHANNELS.RUNTIME.GET_STATUS),
ensure: (params: { component: RuntimeComponentName }) =>
invoke<{ component: RuntimeComponentName; binaryPath: string }>(
IPC_CHANNELS.RUNTIME.ENSURE,
params,
),
onProgress: (cb: (e: RuntimeProgressPayload) => void): Unsubscribe =>
on(IPC_CHANNELS.RUNTIME.PROGRESS, cb)
},
// ── Hotkey ─────────────────────────────────────────────
hotkey: {
getDictationShortcut: () =>
invoke<HotkeyBinding>(IPC_CHANNELS.HOTKEY.GET_DICTATION_SHORTCUT),
setDictationShortcut: (params: SetHotkeyParams) =>
invoke<void>(IPC_CHANNELS.HOTKEY.SET_DICTATION_SHORTCUT, params),
getHandsFreeShortcut: () =>
invoke<HotkeyBinding>(IPC_CHANNELS.HOTKEY.GET_HANDS_FREE_SHORTCUT),
setHandsFreeShortcut: (params: SetHotkeyParams) =>
invoke<void>(IPC_CHANNELS.HOTKEY.SET_HANDS_FREE_SHORTCUT, params),
getCommandShortcut: () =>
invoke<HotkeyBinding>(IPC_CHANNELS.HOTKEY.GET_COMMAND_SHORTCUT),
setCommandShortcut: (params: SetHotkeyParams) =>
invoke<void>(IPC_CHANNELS.HOTKEY.SET_COMMAND_SHORTCUT, params),
getCaptionShortcut: () =>
invoke<HotkeyBinding>(IPC_CHANNELS.HOTKEY.GET_CAPTION_SHORTCUT),
setCaptionShortcut: (params: SetHotkeyParams) =>
invoke<void>(IPC_CHANNELS.HOTKEY.SET_CAPTION_SHORTCUT, params),
isEnabled: () => invoke<boolean>(IPC_CHANNELS.HOTKEY.IS_ENABLED),
setEnabled: (params: SetEnabledParams) =>
invoke<void>(IPC_CHANNELS.HOTKEY.SET_ENABLED, params),
onTriggered: (cb: (e: HotkeyTriggeredEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.HOTKEY.TRIGGERED, cb)
},
// ── LLM ────────────────────────────────────────────────
llm: {
getStatus: () => invoke<LLMStatus>(IPC_CHANNELS.LLM.GET_STATUS),
getModels: () => invoke<LLMModel[]>(IPC_CHANNELS.LLM.GET_MODELS),
getActiveModel: () => invoke<string | null>(IPC_CHANNELS.LLM.GET_ACTIVE_MODEL),
setModel: (params: SetLLMModelParams) =>
invoke<void>(IPC_CHANNELS.LLM.SET_MODEL, params),
process: (params: LLMProcessParams) =>
invoke<LLMProcessResult>(IPC_CHANNELS.LLM.PROCESS, params),
cancelProcess: () => invoke<void>(IPC_CHANNELS.LLM.CANCEL_PROCESS),
getServerUrl: () => invoke<string>(IPC_CHANNELS.LLM.GET_SERVER_URL),
setServerUrl: (params: SetServerUrlParams) =>
invoke<void>(IPC_CHANNELS.LLM.SET_SERVER_URL, params),
onStatusChanged: (cb: (e: LLMStatusChangedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.LLM.STATUS_CHANGED, cb),
onProcessProgress: (cb: (e: LLMProcessProgressEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.LLM.PROCESS_PROGRESS, cb),
pullModel: (params: { modelId: string }) =>
invoke<void>(IPC_CHANNELS.LLM.PULL_MODEL, params),
startServer: () =>
invoke<'running' | 'starting' | 'not-installed' | 'failed'>(
IPC_CHANNELS.LLM.START_SERVER
),
checkConnection: () =>
invoke<{ available: boolean; version: string | null; models: LLMModel[] }>(
IPC_CHANNELS.LLM.CHECK_CONNECTION
),
onPullProgress: (
cb: (e: {
modelId: string
status: string
digest: string | null
total: number
completed: number
percent: number
}) => void
): Unsubscribe => on(IPC_CHANNELS.LLM.PULL_PROGRESS, cb),
// Phase 3.2: Premium LLM
premium: {
getStatus: () =>
invoke<{ available: boolean; backend: 'local' | 'premium' }>(
IPC_CHANNELS.LLM.PREMIUM_GET_STATUS
),
getQuota: () =>
invoke<{
tier: 'free' | 'pro' | 'pro_plus'
current: number
limit: number
overageCredits: number
} | null>(IPC_CHANNELS.LLM.PREMIUM_GET_QUOTA),
onFallback: (cb: (e: { reason: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.LLM.PREMIUM_FALLBACK, cb),
onQuotaWarning: (
cb: (e: { current: number; limit: number; overageCredits: number }) => void
): Unsubscribe => on(IPC_CHANNELS.LLM.PREMIUM_QUOTA_WARNING, cb),
onUpgradeRequired: (
cb: (e: { reason: 'quota_exceeded' | 'model_not_allowed' | 'auth_required' }) => void
): Unsubscribe => on(IPC_CHANNELS.LLM.PREMIUM_UPGRADE_REQUIRED, cb),
},
},
// ── History ────────────────────────────────────────────
history: {
getAll: (params: HistoryQueryParams) =>
invoke<HistoryPage>(IPC_CHANNELS.HISTORY.GET_ALL, params),
getById: (params: HistoryGetByIdParams) =>
invoke<HistoryEntry | null>(IPC_CHANNELS.HISTORY.GET_BY_ID, params),
delete: (params: HistoryDeleteParams) =>
invoke<void>(IPC_CHANNELS.HISTORY.DELETE, params),
deleteAll: () => invoke<void>(IPC_CHANNELS.HISTORY.DELETE_ALL),
search: (params: HistorySearchParams) =>
invoke<HistoryPage>(IPC_CHANNELS.HISTORY.SEARCH, params),
onAdded: (cb: (e: HistoryEntry) => void): Unsubscribe =>
on(IPC_CHANNELS.HISTORY.ADDED, cb)
},
// ── Dictionary ─────────────────────────────────────────
dictionary: {
getAll: (params: DictionaryQueryParams) =>
invoke<DictionaryPage>(IPC_CHANNELS.DICTIONARY.GET_ALL, params),
add: (params: DictionaryAddParams) =>
invoke<DictionaryEntry>(IPC_CHANNELS.DICTIONARY.ADD, params),
update: (params: DictionaryUpdateParams) =>
invoke<DictionaryEntry>(IPC_CHANNELS.DICTIONARY.UPDATE, params),
delete: (params: DictionaryDeleteParams) =>
invoke<void>(IPC_CHANNELS.DICTIONARY.DELETE, params),
search: (params: DictionarySearchParams) =>
invoke<DictionaryPage>(IPC_CHANNELS.DICTIONARY.SEARCH, params),
import: (params: DictionaryImportParams) =>
invoke<DictionaryImportResult>(IPC_CHANNELS.DICTIONARY.IMPORT, params),
export: (params: DictionaryExportParams) =>
invoke<string>(IPC_CHANNELS.DICTIONARY.EXPORT, params)
},
// ── Stats ──────────────────────────────────────────────
stats: {
getSummary: () => invoke<StatsSummary>(IPC_CHANNELS.STATS.GET_SUMMARY)
},
// ── Window ─────────────────────────────────────────────
window: {
minimize: () => send(IPC_CHANNELS.WINDOW.MINIMIZE),
maximize: () => send(IPC_CHANNELS.WINDOW.MAXIMIZE),
close: () => send(IPC_CHANNELS.WINDOW.CLOSE),
isMaximized: () => invoke<boolean>(IPC_CHANNELS.WINDOW.IS_MAXIMIZED)
},
// ── System ─────────────────────────────────────────────
system: {
getPlatform: () => invoke<NodeJS.Platform>(IPC_CHANNELS.SYSTEM.GET_PLATFORM),
getVersion: () => invoke<string>(IPC_CHANNELS.SYSTEM.GET_VERSION),
checkMicPermission: () =>
invoke<PermissionStatus>(IPC_CHANNELS.SYSTEM.CHECK_MIC_PERMISSION),
openExternal: (params: { url: string }) =>
invoke<void>(IPC_CHANNELS.SYSTEM.OPEN_EXTERNAL, params),
},
// ── Instruction (커스텀 명령어) ────────────────────────
instruction: {
getAll: () => invoke<unknown[]>('instruction:getAll'),
getById: (params: { id: string }) => invoke<unknown>('instruction:getById', params),
create: (params: { name: string; description: string; prompt: string }) =>
invoke<unknown>('instruction:create', params),
update: (params: { id: string; name?: string; description?: string; prompt?: string }) =>
invoke<unknown>('instruction:update', params),
delete: (params: { id: string }) => invoke<void>('instruction:delete', params),
reorder: (params: { ids: string[] }) => invoke<void>('instruction:reorder', params),
},
// ── App 이벤트 (실시간 UI 갱신) ────────────────────────
app: {
onDataChanged: (cb: (data: { type: string; activeId?: string }) => void): Unsubscribe =>
on('app:dataChanged', cb),
},
// ── Phase 10: Memo Tags ─────────────────────────────────
memo: {
getTags: (params: GetTagsParams) => invoke<MemoTag[]>(IPC_CHANNELS.MEMO.GET_TAGS, params),
addTag: (params: AddTagParams) => invoke<MemoTag>(IPC_CHANNELS.MEMO.ADD_TAG, params),
removeTag: (params: RemoveTagParams) => invoke<void>(IPC_CHANNELS.MEMO.REMOVE_TAG, params),
getAllTags: () => invoke<TagCount[]>(IPC_CHANNELS.MEMO.GET_ALL_TAGS),
searchByTag: (params: SearchByTagParams) => invoke<HistoryPage>(IPC_CHANNELS.MEMO.SEARCH_BY_TAG, params),
export: (params: ExportMemoParams) => invoke<string>(IPC_CHANNELS.MEMO.EXPORT, params),
},
// ── Phase 10: Voice Commands ────────────────────────────
voiceCommand: {
getAll: () => invoke<VoiceCommandRule[]>(IPC_CHANNELS.VOICE_COMMAND.GET_ALL),
setKeywords: (params: SetVoiceCommandKeywordsParams) => invoke<void>(IPC_CHANNELS.VOICE_COMMAND.SET_KEYWORDS, params),
setEnabled: (params: SetVoiceCommandEnabledParams) => invoke<void>(IPC_CHANNELS.VOICE_COMMAND.SET_ENABLED, params),
isEnabled: () => invoke<boolean>(IPC_CHANNELS.VOICE_COMMAND.IS_ENABLED),
onMatched: (cb: (data: VoiceCommandMatch) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_COMMAND.MATCHED, cb),
},
// ── Phase 10: Screen Context ────────────────────────────
context: {
capture: (captureSelectedText?: boolean) => invoke<CaptureContextResult>(IPC_CHANNELS.CONTEXT.CAPTURE, { captureSelectedText }),
isEnabled: () => invoke<boolean>(IPC_CHANNELS.CONTEXT.IS_ENABLED),
setEnabled: (params: { enabled: boolean }) => invoke<void>(IPC_CHANNELS.CONTEXT.SET_ENABLED, params),
},
// ── Phase 10: LLM Chain ─────────────────────────────────
chain: {
getAll: () => invoke<LLMChain[]>(IPC_CHANNELS.CHAIN.GET_ALL),
create: (params: CreateChainParams) => invoke<LLMChain>(IPC_CHANNELS.CHAIN.CREATE, params),
update: (params: UpdateChainParams) => invoke<LLMChain>(IPC_CHANNELS.CHAIN.UPDATE, params),
delete: (params: DeleteChainParams) => invoke<void>(IPC_CHANNELS.CHAIN.DELETE, params),
execute: (params: ExecuteChainParams) => invoke<ChainExecutionResult>(IPC_CHANNELS.CHAIN.EXECUTE, params),
onProgress: (cb: (data: ChainProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.CHAIN.PROGRESS, cb),
},
// ── Phase 10: Live Caption ──────────────────────────────
caption: {
start: () => invoke<void>(IPC_CHANNELS.CAPTION.START),
stop: () => invoke<void>(IPC_CHANNELS.CAPTION.STOP),
getState: () => invoke<CaptionState>(IPC_CHANNELS.CAPTION.GET_STATE),
setConfig: (params: Partial<CaptionConfig>) => invoke<void>(IPC_CHANNELS.CAPTION.SET_CONFIG, params),
getConfig: () => invoke<CaptionConfig>(IPC_CHANNELS.CAPTION.GET_CONFIG),
onSegment: (cb: (data: CaptionSegment) => void): Unsubscribe =>
on(IPC_CHANNELS.CAPTION.SEGMENT, cb),
onDelta: (cb: (data: { text: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.CAPTION.DELTA, cb),
onStateChanged: (cb: (data: { state: CaptionState }) => void): Unsubscribe =>
on(IPC_CHANNELS.CAPTION.STATE_CHANGED, cb),
/** 시스템 오디오 PCM 데이터를 메인에 전달 (렌더러 → 메인) */
sendSystemAudioData: (data: ArrayBuffer) =>
send(IPC_CHANNELS.CAPTION.SYSTEM_AUDIO_DATA, data),
/** 메인에서 시스템 오디오 캡처 시작 요청 수신 */
onStartSystemAudio: (cb: () => void): Unsubscribe =>
on(IPC_CHANNELS.CAPTION.START_SYSTEM_AUDIO, cb),
/** 메인에서 시스템 오디오 캡처 중지 요청 수신 */
onStopSystemAudio: (cb: () => void): Unsubscribe =>
on(IPC_CHANNELS.CAPTION.STOP_SYSTEM_AUDIO, cb),
/** 시스템 오디오 루프백 활성화 (getDisplayMedia 전에 호출) */
enableLoopback: () => invoke<void>('system-audio:enable-loopback'),
/** 시스템 오디오 루프백 비활성화 */
disableLoopback: () => invoke<void>('system-audio:disable-loopback'),
},
// ── License (Phase 11) ────────────────────────────────
license: {
getInfo: () =>
invoke<LicenseInfo>(IPC_CHANNELS.LICENSE.GET_INFO),
activate: (params: ActivateLicenseParams) =>
invoke<ActivateLicenseResult>(IPC_CHANNELS.LICENSE.ACTIVATE, params),
deactivate: () =>
invoke<void>(IPC_CHANNELS.LICENSE.DEACTIVATE),
checkFeature: (feature: Feature) =>
invoke<FeatureAccess>(IPC_CHANNELS.LICENSE.CHECK_FEATURE, { feature }),
getUsage: (feature: Feature) =>
invoke<UsageQuota>(IPC_CHANNELS.LICENSE.GET_USAGE, { feature }),
getAllUsage: () =>
invoke<UsageQuota[]>(IPC_CHANNELS.LICENSE.GET_ALL_USAGE),
getTierComparison: () =>
invoke<TierComparison[]>(IPC_CHANNELS.LICENSE.GET_TIER_COMPARISON),
openBilling: (params: { tier: 'pro' | 'pro_plus' }) =>
invoke<void>(IPC_CHANNELS.LICENSE.OPEN_BILLING, params),
onUpgradePrompt: (cb: (e: UpgradePromptEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.LICENSE.UPGRADE_PROMPT, cb),
onTierChanged: (cb: (e: LicenseInfo) => void): Unsubscribe =>
on(IPC_CHANNELS.LICENSE.TIER_CHANGED, cb),
},
// ── File Transcription (Phase 12.1) ────────────────────
fileTranscription: {
start: (params: FileTranscriptionStartParams) =>
invoke<FileTranscriptionResult>(IPC_CHANNELS.FILE_TRANSCRIPTION.START, params),
cancel: () =>
invoke<void>(IPC_CHANNELS.FILE_TRANSCRIPTION.CANCEL),
getState: () =>
invoke<FileTranscriptionStateInfo>(IPC_CHANNELS.FILE_TRANSCRIPTION.GET_STATE),
onProgress: (cb: (data: FileTranscriptionProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.FILE_TRANSCRIPTION.PROGRESS, cb),
onComplete: (cb: (data: FileTranscriptionResult) => void): Unsubscribe =>
on(IPC_CHANNELS.FILE_TRANSCRIPTION.COMPLETE, cb),
onError: (cb: (data: { message: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.FILE_TRANSCRIPTION.ERROR, cb),
},
// ── Meeting Summary (Phase 12.2) ─────────────────────
meetingSummary: {
summarize: (params: MeetingSummarizeParams) =>
invoke<MeetingSummaryResult>(IPC_CHANNELS.MEETING_SUMMARY.SUMMARIZE, params),
getSummary: (params: MeetingSummaryGetParams) =>
invoke<MeetingSummaryResult | null>(IPC_CHANNELS.MEETING_SUMMARY.GET_SUMMARY, params),
exportMarkdown: (params: MeetingSummaryExportParams) =>
invoke<string>(IPC_CHANNELS.MEETING_SUMMARY.EXPORT_MARKDOWN, params),
onSummaryReady: (cb: (data: MeetingSummaryResult) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_SUMMARY.SUMMARY_READY, cb),
onProgress: (cb: (data: MeetingSummaryProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_SUMMARY.SUMMARY_PROGRESS, cb),
},
// ── Dictation Templates (Phase 12.3) ─────────────────
dictationTemplate: {
getAll: () =>
invoke<DictationTemplate[]>(IPC_CHANNELS.DICTATION_TEMPLATE.GET_ALL),
create: (params: CreateTemplateParams) =>
invoke<DictationTemplate>(IPC_CHANNELS.DICTATION_TEMPLATE.CREATE, params),
update: (params: UpdateTemplateParams) =>
invoke<DictationTemplate>(IPC_CHANNELS.DICTATION_TEMPLATE.UPDATE, params),
delete: (params: DeleteTemplateParams) =>
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.DELETE, params),
startSession: (params: StartTemplateSessionParams) =>
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.START_SESSION, params),
cancelSession: () =>
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.CANCEL_SESSION),
getSessionState: () =>
invoke<TemplateSessionInfo | null>(IPC_CHANNELS.DICTATION_TEMPLATE.GET_SESSION_STATE),
setFieldValue: (params: SetFieldValueParams) =>
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.SET_FIELD_VALUE, params),
onSessionStateChanged: (cb: (data: TemplateSessionInfo) => void): Unsubscribe =>
on(IPC_CHANNELS.DICTATION_TEMPLATE.SESSION_STATE_CHANGED, cb),
onFieldCompleted: (cb: (data: TemplateFieldCompletedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.DICTATION_TEMPLATE.FIELD_COMPLETED, cb),
onSessionCompleted: (cb: (data: TemplateSessionCompletedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.DICTATION_TEMPLATE.SESSION_COMPLETED, cb),
},
// ── RAG (Phase 13.2) ──────────────────────────────────
rag: {
addDocument: (params?: RAGAddDocumentParams) =>
invoke<RAGDocument>(IPC_CHANNELS.RAG.ADD_DOCUMENT, params ?? {}),
removeDocument: (params: RAGRemoveDocumentParams) =>
invoke<void>(IPC_CHANNELS.RAG.REMOVE_DOCUMENT, params),
getDocuments: () =>
invoke<RAGDocument[]>(IPC_CHANNELS.RAG.GET_DOCUMENTS),
query: (params: RAGQueryParams) =>
invoke<RAGQueryResult>(IPC_CHANNELS.RAG.QUERY, params),
getState: () =>
invoke<RAGStateInfo>(IPC_CHANNELS.RAG.GET_STATE),
reindex: (documentId: string) =>
invoke<void>(IPC_CHANNELS.RAG.REINDEX, { documentId }),
onIndexProgress: (cb: (data: RAGIndexProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.RAG.INDEX_PROGRESS, cb),
onIndexComplete: (cb: (data: { documentId: string; fileName: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.RAG.INDEX_COMPLETE, cb),
},
// ── Voice Action (Phase 13.3) ────────────────────────
voiceAction: {
execute: (params: VoiceActionExecuteParams) =>
invoke<void>(IPC_CHANNELS.VOICE_ACTION.EXECUTE, params),
getPresets: () =>
invoke<VoiceActionPreset[]>(IPC_CHANNELS.VOICE_ACTION.GET_PRESETS),
getHistory: () =>
invoke<VoiceActionHistoryEntry[]>(IPC_CHANNELS.VOICE_ACTION.GET_HISTORY),
clearHistory: () =>
invoke<void>(IPC_CHANNELS.VOICE_ACTION.CLEAR_HISTORY),
setEnabled: (enabled: boolean) =>
invoke<void>(IPC_CHANNELS.VOICE_ACTION.SET_ENABLED, { enabled }),
isEnabled: () =>
invoke<boolean>(IPC_CHANNELS.VOICE_ACTION.IS_ENABLED),
onActionPlanned: (cb: (data: VoiceActionPlannedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_ACTION.ACTION_PLANNED, cb),
onActionExecuted: (cb: (data: VoiceActionExecutedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_ACTION.ACTION_EXECUTED, cb),
onActionError: (cb: (data: VoiceActionErrorEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_ACTION.ACTION_ERROR, cb),
},
// ── Voice Conversation (Phase 13.1) ───────────────────
voiceConversation: {
startSession: () =>
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.START_SESSION),
stopSession: () =>
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.STOP_SESSION),
sendMessage: (params: ConversationSendParams) =>
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.SEND_MESSAGE, params),
getState: () =>
invoke<ConversationSessionInfo>(IPC_CHANNELS.VOICE_CONVERSATION.GET_STATE),
getHistory: () =>
invoke<ConversationMessage[]>(IPC_CHANNELS.VOICE_CONVERSATION.GET_HISTORY),
clearHistory: () =>
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.CLEAR_HISTORY),
cancelResponse: () =>
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.CANCEL_RESPONSE),
getRealtimeToken: (params: RealtimeTokenParams) =>
invoke<RealtimeTokenResult>(
IPC_CHANNELS.VOICE_CONVERSATION.GET_REALTIME_TOKEN,
params
),
finishListening: () =>
invoke<void>('voiceConversation:finishListening'),
onStateChanged: (cb: (data: ConversationSessionInfo) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.STATE_CHANGED, cb),
onUserMessage: (cb: (data: ConversationMessage) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.USER_MESSAGE, cb),
onAssistantDelta: (cb: (data: ConversationAssistantDelta) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.ASSISTANT_DELTA, cb),
onAssistantMessage: (cb: (data: ConversationAssistantMessage) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.ASSISTANT_MESSAGE, cb),
onTTSStarted: (cb: () => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.TTS_STARTED, cb),
onTTSFinished: (cb: () => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.TTS_FINISHED, cb),
onError: (cb: (data: ConversationError) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.ERROR, cb),
onAudioLevel: (cb: (e: { level: number }) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.AUDIO_LEVEL, cb),
},
// ── Meeting Mode (Phase 14) ───────────────────────────
meetingMode: {
startRecording: (params?: { force?: boolean }) =>
invoke<MeetingStartResult>(IPC_CHANNELS.MEETING_MODE.START_RECORDING, params),
forceReset: () =>
invoke<void>('meetingMode:forceReset'),
stopRecording: () =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.STOP_RECORDING),
addMemo: (params: MeetingAddMemoParams) =>
invoke<MeetingMemo>(IPC_CHANNELS.MEETING_MODE.ADD_MEMO, params),
getState: () =>
invoke<MeetingModeStateInfo>(IPC_CHANNELS.MEETING_MODE.GET_STATE),
getSessions: (params: MeetingGetSessionsParams) =>
invoke<MeetingSessionPage>(IPC_CHANNELS.MEETING_MODE.GET_SESSIONS, params),
getSession: (params: MeetingGetSessionParams) =>
invoke<MeetingSessionDetail>(IPC_CHANNELS.MEETING_MODE.GET_SESSION, params),
deleteSession: (params: MeetingDeleteSessionParams) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.DELETE_SESSION, params),
updateTitle: (params: { sessionId: string; title: string }) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.UPDATE_TITLE, params),
exportPdf: (params: MeetingExportParams) =>
invoke<string>(IPC_CHANNELS.MEETING_MODE.EXPORT_PDF, params),
exportMarkdown: (params: MeetingExportParams) =>
invoke<string>(IPC_CHANNELS.MEETING_MODE.EXPORT_MARKDOWN, params),
onStateChanged: (cb: (e: MeetingModeStateInfo) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.STATE_CHANGED, cb),
onSegment: (cb: (e: CaptionSegment) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.SEGMENT, cb),
onProcessingProgress: (cb: (e: MeetingProcessingProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.PROCESSING_PROGRESS, cb),
onSessionCompleted: (cb: (e: MeetingSessionDetail) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.SESSION_COMPLETED, cb),
onError: (cb: (e: { code: number; message: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.ERROR, cb),
onAudioLevel: (cb: (e: { level: number }) => void): Unsubscribe =>
on('meetingMode:audioLevel', cb),
// Phase 14.5
updateTranscript: (params: MeetingUpdateTranscriptParams) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.UPDATE_TRANSCRIPT, params),
generateDocument: (params: MeetingGenerateDocParams) =>
invoke<MeetingDocument>(IPC_CHANNELS.MEETING_MODE.GENERATE_DOCUMENT, params),
getDocuments: (params: MeetingGetDocsParams) =>
invoke<MeetingDocument[]>(IPC_CHANNELS.MEETING_MODE.GET_DOCUMENTS, params),
updateDocument: (params: MeetingUpdateDocParams) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.UPDATE_DOCUMENT, params),
deleteDocument: (params: MeetingDeleteDocParams) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.DELETE_DOCUMENT, params),
exportDocument: (params: MeetingExportDocParams) =>
invoke<string>(IPC_CHANNELS.MEETING_MODE.EXPORT_DOCUMENT, params),
exportTranscript: (params: MeetingExportTranscriptParams) =>
invoke<string>(IPC_CHANNELS.MEETING_MODE.EXPORT_TRANSCRIPT, params),
editSegment: (params: { sessionId: string; segmentId: string; text: string }) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.EDIT_SEGMENT, params),
onDocGeneratingProgress: (cb: (e: MeetingDocGeneratingProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.DOC_GENERATING_PROGRESS, cb),
// Phase 15
polishTranscript: (params: { sessionId: string }) =>
invoke<string>(IPC_CHANNELS.MEETING_MODE.POLISH_TRANSCRIPT, params),
// Phase 15.5
diarize: (params: DiarizeSessionParams) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.DIARIZE, params),
onDiarizationProgress: (cb: (e: { sessionId: string; percent: number }) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.DIARIZATION_PROGRESS, cb),
},
// ── Meeting Chat (Phase 15) ───────────────────────────
meetingChat: {
send: (params: MeetingChatSendParams) =>
invoke<void>(IPC_CHANNELS.MEETING_CHAT.SEND, params),
cancel: () =>
invoke<void>(IPC_CHANNELS.MEETING_CHAT.CANCEL),
clear: () =>
invoke<void>(IPC_CHANNELS.MEETING_CHAT.CLEAR),
onDelta: (cb: (e: MeetingChatDelta) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_CHAT.DELTA, cb),
onMessage: (cb: (e: MeetingChatMessage) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_CHAT.MESSAGE, cb),
onError: (cb: (e: { message: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_CHAT.ERROR, cb),
},
// ── Meeting Doc Templates (Phase 14.5) ───────────────
meetingDocTemplate: {
getAll: () =>
invoke<MeetingDocTemplate[]>(IPC_CHANNELS.MEETING_DOC_TEMPLATE.GET_ALL),
create: (params: CreateMeetingDocTemplateParams) =>
invoke<MeetingDocTemplate>(IPC_CHANNELS.MEETING_DOC_TEMPLATE.CREATE, params),
update: (params: UpdateMeetingDocTemplateParams) =>
invoke<MeetingDocTemplate>(IPC_CHANNELS.MEETING_DOC_TEMPLATE.UPDATE, params),
delete: (params: DeleteMeetingDocTemplateParams) =>
invoke<void>(IPC_CHANNELS.MEETING_DOC_TEMPLATE.DELETE, params),
},
cloudSync: {
getState: () =>
invoke<{
authenticated: boolean
userEmail: string | null
lastSyncAt: number | null
syncing: boolean
}>(IPC_CHANNELS.CLOUD_SYNC.GET_STATE),
signIn: (params: { provider: 'google' | 'github' }) =>
invoke<{ started: boolean }>(IPC_CHANNELS.CLOUD_SYNC.SIGN_IN, params),
signOut: () => invoke<unknown>(IPC_CHANNELS.CLOUD_SYNC.SIGN_OUT),
pushAll: () =>
invoke<{ pushed: number; errors: string[] }>(IPC_CHANNELS.CLOUD_SYNC.PUSH_ALL),
pullAll: () =>
invoke<{ pushed: number; errors: string[] }>(IPC_CHANNELS.CLOUD_SYNC.PULL_ALL),
onAuthChanged: (callback: (data: { user: { id: string; email: string | null } | null }) => void) =>
on<{ user: { id: string; email: string | null } | null }>(
IPC_CHANNELS.CLOUD_SYNC.AUTH_CHANGED,
callback
),
onSyncProgress: (callback: (data: { current: number; total: number; table: string }) => void) =>
on<{ current: number; total: number; table: string }>(
IPC_CHANNELS.CLOUD_SYNC.SYNC_PROGRESS,
callback
),
onSyncComplete: (callback: (data: { pushed: number; errors: string[] }) => void) =>
on<{ pushed: number; errors: string[] }>(IPC_CHANNELS.CLOUD_SYNC.SYNC_COMPLETE, callback),
onSyncError: (callback: (data: { error: string }) => void) =>
on<{ error: string }>(IPC_CHANNELS.CLOUD_SYNC.SYNC_ERROR, callback),
},
// ── Online Auth ────────────────────────────────────────
onlineAuth: {
register: (params: { email: string; password: string }) =>
invoke<unknown>(IPC_CHANNELS.ONLINE_AUTH.REGISTER, params),
login: (params: { email: string; password: string }) =>
invoke<unknown>(IPC_CHANNELS.ONLINE_AUTH.LOGIN, params),
logout: () => invoke<void>(IPC_CHANNELS.ONLINE_AUTH.LOGOUT),
getUser: () =>
invoke<{ email: string | null; isAuthenticated: boolean; mode: 'online' | 'local' | null }>(
IPC_CHANNELS.ONLINE_AUTH.GET_USER
)
},
// ── Ad Monetization & Mediation ────────────────────────
ads: {
getConfig: () => invoke<import('@d3ro/core/types').AdMediationConfig>(IPC_CHANNELS.ADS.GET_CONFIG),
setConfig: (config: Partial<import('@d3ro/core/types').AdMediationConfig>) =>
invoke<import('@d3ro/core/types').AdMediationConfig>(IPC_CHANNELS.ADS.SET_CONFIG, config),
requestAuction: (request: import('@d3ro/core/types').AdMediationAuctionRequest) =>
invoke<import('@d3ro/core/types').AdMediationAuctionResult>(IPC_CHANNELS.ADS.REQUEST_AUCTION, request),
recordImpression: (event: Omit<import('@d3ro/core/types').AdImpressionEvent, 'timestamp'>) =>
invoke<boolean>(IPC_CHANNELS.ADS.RECORD_IMPRESSION, event),
recordClick: (params: { adId: string; networkId: string }) =>
invoke<boolean>(IPC_CHANNELS.ADS.RECORD_CLICK, params),
claimReward: (params: { adId: string; networkId: string }) =>
invoke<import('@d3ro/core/types').AdRewardResult>(IPC_CHANNELS.ADS.CLAIM_REWARD, params),
getRevenueStats: (params?: { period?: string }) =>
invoke<import('@d3ro/core/types').AdRevenueStats>(IPC_CHANNELS.ADS.GET_REVENUE_STATS, params),
getSettlements: () =>
invoke<import('@d3ro/core/types').AdSettlementRecord[]>(IPC_CHANNELS.ADS.GET_SETTLEMENTS),
requestPayout: (params: { settlementId: string }) =>
invoke<{ success: boolean; message: string; settlement?: import('@d3ro/core/types').AdSettlementRecord }>(
IPC_CHANNELS.ADS.REQUEST_PAYOUT,
params
),
getPublisherAccount: () =>
invoke<import('@d3ro/core/types').PublisherAccountConfig>(IPC_CHANNELS.ADS.GET_PUBLISHER_ACCOUNT),
setPublisherAccount: (account: Partial<import('@d3ro/core/types').PublisherAccountConfig>) =>
invoke<import('@d3ro/core/types').PublisherAccountConfig>(IPC_CHANNELS.ADS.SET_PUBLISHER_ACCOUNT, account),
},
// ── Customer Assistance (CA/CS) & Diagnostics ──────────
support: {
getDiagnostics: () =>
invoke<import('@d3ro/core/types').SystemDiagnosticsPayload>(IPC_CHANNELS.SUPPORT.GET_DIAGNOSTICS),
queryAI: (params: import('@d3ro/core/types').AIAssistQuery) =>
invoke<import('@d3ro/core/types').AIAssistResponse>(IPC_CHANNELS.SUPPORT.QUERY_AI, params),
createTicket: (params: Partial<import('@d3ro/core/types').SupportTicket>) =>
invoke<import('@d3ro/core/types').SupportTicket>(IPC_CHANNELS.SUPPORT.CREATE_TICKET, params),
checkRefund: () =>
invoke<import('@d3ro/core/types').RefundEligibilityResult>(IPC_CHANNELS.SUPPORT.CHECK_REFUND),
},
// ── Multi-PG Payment & Billing ─────────────────────────
payment: {
createCheckoutSession: (params: import('@d3ro/core/types').CheckoutSessionParams) =>
invoke<import('@d3ro/core/types').CheckoutSessionResult>(IPC_CHANNELS.PAYMENT.CREATE_CHECKOUT_SESSION, params),
verifyPayment: () =>
invoke<import('@d3ro/core/types').VerifyPaymentResult>(IPC_CHANNELS.PAYMENT.VERIFY_PAYMENT),
getSubscriptionStatus: () =>
invoke<import('@d3ro/core/types').SubscriptionStatusResult>(IPC_CHANNELS.PAYMENT.GET_SUBSCRIPTION_STATUS),
},
} as const
contextBridge.exposeInMainWorld('electronAPI', electronAPI)
export type ElectronAPI = typeof electronAPI