// src/shared/errors.ts export enum ErrorCode { // === Success === Success = 0, // === STT (100-199) === STTEngineNotInstalled = 100, STTModelNotFound = 101, STTModelNotLoaded = 102, STTModelLoadFailed = 103, STTModelDownloadFailed = 104, STTModelDownloadCancelled = 105, STTTranscriptionFailed = 110, STTTranscriptionTimeout = 111, STTTranscriptionCancelled = 112, STTNoAudioData = 113, STTAudioTooShort = 114, STTLanguageNotSupported = 120, STTSidecarSpawnFailed = 130, STTSidecarCrashed = 131, STTSidecarCommunicationFailed = 132, STTGPUNotAvailable = 140, // === TTS (200-299) === TTSEngineNotInstalled = 200, TTSVoiceNotFound = 201, TTSVoiceNotLoaded = 202, TTSVoiceLoadFailed = 203, TTSVoiceDownloadFailed = 204, TTSSynthesisFailed = 210, TTSPlaybackFailed = 211, TTSPlaybackInterrupted = 212, TTSTextTooLong = 220, TTSTextEmpty = 221, // === LLM / Ollama (300-399) === LLMServerUnreachable = 300, LLMServerConnectionFailed = 301, LLMServerTimeout = 302, LLMModelNotFound = 310, LLMModelNotLoaded = 311, LLMModelLoadFailed = 312, LLMModelPullFailed = 313, LLMModelPullCancelled = 314, LLMProcessingFailed = 320, LLMProcessingTimeout = 321, LLMProcessingCancelled = 322, LLMResponseParseFailed = 323, LLMInvalidAction = 330, LLMPromptTooLong = 331, // === Audio (400-499) === AudioDeviceNotFound = 400, AudioDeviceAccessDenied = 401, AudioDeviceBusy = 402, AudioCaptureStartFailed = 410, AudioCaptureStopFailed = 411, AudioCaptureFailed = 412, AudioNoPermission = 420, AudioStreamError = 430, AudioBufferOverflow = 431, // === Hotkey (500-599) === HotkeyRegistrationFailed = 500, HotkeyConflict = 501, HotkeySystemReserved = 502, HotkeyHookInitFailed = 510, HotkeyHookCrashed = 511, // === TextInsert (600-699) === TextInsertFailed = 600, TextInsertClipboardSaveFailed = 601, TextInsertClipboardRestoreFailed = 602, TextInsertKeySimulationFailed = 603, TextInsertNoActiveWindow = 610, TextInsertTargetAppNotResponding = 611, // === History / Dictionary / DB (700-799) === DBOpenFailed = 700, DBMigrationFailed = 701, DBQueryFailed = 702, DBWriteFailed = 703, HistoryNotFound = 710, HistoryExportFailed = 711, DictionaryNotFound = 720, DictionaryDuplicate = 721, DictionaryImportFailed = 722, DictionaryExportFailed = 723, DictionaryImportInvalidFormat = 724, // === Phase 10: Memo Tags (730-739) === MemoTagDuplicate = 730, MemoTagNotFound = 731, MemoExportFailed = 732, // === Phase 10: Voice Commands (740-749) === VoiceCommandMatchFailed = 740, VoiceCommandNotFound = 741, // === Phase 10: Screen Context (750-759) === ContextCaptureFailed = 750, ContextSelectedTextFailed = 751, // === Phase 10: LLM Chain (760-769) === ChainNotFound = 760, ChainExecutionFailed = 761, ChainStepFailed = 762, ChainCancelled = 763, // === Phase 10: Live Caption (770-779) === CaptionStartFailed = 770, CaptionAlreadyActive = 771, CaptionSTTFailed = 772, // === Phase 12: File Transcription (780-784) === FileTranscriptionFFmpegFailed = 780, FileTranscriptionInvalidFormat = 781, FileTranscriptionChunkFailed = 782, FileTranscriptionCancelled = 783, FileTranscriptionFileTooLarge = 784, // === Phase 12: Meeting Summary (785-789) === MeetingSummaryGenerationFailed = 785, MeetingSummaryNoTranscript = 786, MeetingSummaryExportFailed = 787, // === Phase 12: Dictation Template (790-794) === TemplateNotFound = 790, TemplateSessionAlreadyActive = 791, TemplateSessionNotActive = 792, TemplateFieldRecordingFailed = 793, TemplateInvalidFormat = 794, // === Phase 13: Voice Conversation (795-799) === ConversationSessionAlreadyActive = 795, ConversationNoActiveSession = 796, ConversationTTSFailed = 797, ConversationLLMFailed = 798, // === Phase 13: Local RAG (870-874) === RAGDocumentNotFound = 870, RAGIndexingFailed = 871, RAGEmbeddingFailed = 872, RAGQueryFailed = 873, RAGUnsupportedFormat = 874, // === Phase 13: Voice Action (875-879) === VoiceActionPlanFailed = 875, VoiceActionExecutionFailed = 876, VoiceActionBlocked = 877, VoiceActionInvalidPlan = 878, // === Phase 14: Meeting Mode (880-889) === MeetingAlreadyRecording = 880, MeetingNotRecording = 881, MeetingProcessingFailed = 882, MeetingSessionNotFound = 883, MeetingExportFailed = 884, MeetingPdfFailed = 885, // === Config (800-849) === ConfigReadFailed = 800, ConfigWriteFailed = 801, ConfigInvalidValue = 802, ConfigKeyNotFound = 803, ConfigResetFailed = 804, ConfigMigrationFailed = 810, // === License (850-869) === LicenseKeyInvalid = 850, LicenseKeyExpired = 851, LicenseActivationFailed = 852, LicenseDeactivationFailed = 853, LicenseMachineIdMismatch = 854, LicenseOfflineGraceExpired = 855, LicenseVerificationFailed = 856, FeatureNotAvailable = 860, QuotaExceeded = 861, TierRequired = 862, // === System / Window (900-999) === WindowCreationFailed = 900, WindowNotFound = 901, TrayCreationFailed = 910, NotificationFailed = 920, PermissionDenied = 930, ExternalOpenFailed = 940, SoundPlayFailed = 950, AppAlreadyRunning = 960, UnknownError = 999 } /** * D3RO-VOICE 표준 에러 객체 (Speakly NXError 패턴) */ export class D3ROError extends Error { readonly code: ErrorCode readonly details?: Record constructor(code: ErrorCode, message: string, details?: Record) { super(message) this.name = 'D3ROError' this.code = code this.details = details } toJSON(): D3ROErrorJSON { return { code: this.code, message: this.message, details: this.details } } static fromJSON(json: D3ROErrorJSON): D3ROError { return new D3ROError(json.code, json.message, json.details) } } export interface D3ROErrorJSON { code: ErrorCode message: string details?: Record } /** * IPC 핸들러에서 사용하는 표준 응답 래퍼. */ export type IPCResult = | { success: true; data: T } | { success: false; error: D3ROErrorJSON } export function ipcSuccess(data: T): IPCResult { return { success: true, data } } export function ipcError( code: ErrorCode, message: string, details?: Record ): IPCResult { return { success: false, error: { code, message, details } } }