fix(desktop): surface configuration and provider failures instead of hiding them
Several desktop paths quietly substituted defaults or partial results: a config write could fall back to a throwaway in-memory store, speech provider errors were absorbed into empty transcriptions, and meeting exports built file names from raw titles. Writes now fail explicitly when the store is unavailable, provider and model failures reach the UI as errors, and export names pass through one sanitizer. Settings, license, ad, and support surfaces use the shared theme tokens, unused hotkey helpers are gone, and the package gains strict node/renderer typecheck configs plus red-team e2e scenarios for these flows.
This commit is contained in:
parent
c8d802d78f
commit
6ba25f53b7
43 changed files with 1721 additions and 204 deletions
|
|
@ -75,3 +75,14 @@ export function createProbeWav(durationMs: number = 500, sampleRate: number = 16
|
|||
|
||||
return pcmToWav(pcmBuffer, sampleRate, 1, 16)
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer를 fetch 요청 본문으로 넘긴다.
|
||||
*
|
||||
* 런타임에서는 Buffer가 Uint8Array이므로 그대로 본문으로 사용할 수 있지만,
|
||||
* DOM `BodyInit`과 @types/node의 `Buffer<ArrayBufferLike>` 사이에 제네릭
|
||||
* 불일치가 있어 타입 단언이 필요하다.
|
||||
*/
|
||||
export function bufferToBody(data: Buffer): BodyInit {
|
||||
return data as unknown as BodyInit
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,21 @@ import { D3ROError, ErrorCode } from '@d3ro/core/errors'
|
|||
import type { ISTTDriver } from '../types'
|
||||
import type { TranscriptionResult, TranscribeOptions } from '../../LocalSTTService'
|
||||
import type { STTProviderConfig } from '@d3ro/core/types'
|
||||
import { pcmToWav, createProbeWav } from '../audio-utils'
|
||||
import { pcmToWav, createProbeWav, bufferToBody } from '../audio-utils'
|
||||
import { getLogger } from '../../LoggerService'
|
||||
|
||||
const logger = getLogger('AssemblyAIDriver')
|
||||
|
||||
/** AssemblyAI 전사 폴링 응답 (필요한 필드만) */
|
||||
interface AssemblyAITranscriptResponse {
|
||||
status: string
|
||||
text?: string
|
||||
words?: Array<{ text: string; start: number; end: number; confidence: number }>
|
||||
language_code?: string
|
||||
audio_duration?: number
|
||||
error?: string
|
||||
}
|
||||
|
||||
export class AssemblyAIDriver implements ISTTDriver {
|
||||
readonly id = 'assemblyai' as const
|
||||
readonly name = 'AssemblyAI (Universal-2)'
|
||||
|
|
@ -38,7 +48,7 @@ export class AssemblyAIDriver implements ISTTDriver {
|
|||
Authorization: apiKey,
|
||||
'Content-Type': 'application/octet-stream',
|
||||
},
|
||||
body: wavBuffer,
|
||||
body: bufferToBody(wavBuffer),
|
||||
signal: AbortSignal.timeout(20000),
|
||||
})
|
||||
|
||||
|
|
@ -90,14 +100,7 @@ export class AssemblyAIDriver implements ISTTDriver {
|
|||
|
||||
// 3. Poll for result (최대 30초 대기)
|
||||
let attempts = 0
|
||||
let fullResult: {
|
||||
status: string
|
||||
text?: string
|
||||
words?: Array<{ text: string; start: number; end: number; confidence: number }>
|
||||
language_code?: string
|
||||
audio_duration?: number
|
||||
error?: string
|
||||
} | null = null
|
||||
let fullResult: AssemblyAITranscriptResponse | null = null
|
||||
|
||||
while (attempts < 30) {
|
||||
await new Promise((r) => setTimeout(r, 600))
|
||||
|
|
@ -110,7 +113,7 @@ export class AssemblyAIDriver implements ISTTDriver {
|
|||
|
||||
if (!pollRes.ok) continue
|
||||
|
||||
const pollData = (await pollRes.json()) as typeof fullResult
|
||||
const pollData = (await pollRes.json()) as AssemblyAITranscriptResponse
|
||||
if (pollData && (pollData.status === 'completed' || pollData.status === 'error')) {
|
||||
fullResult = pollData
|
||||
break
|
||||
|
|
@ -166,7 +169,7 @@ export class AssemblyAIDriver implements ISTTDriver {
|
|||
Authorization: apiKey,
|
||||
'Content-Type': 'application/octet-stream',
|
||||
},
|
||||
body: probe,
|
||||
body: bufferToBody(probe),
|
||||
signal: AbortSignal.timeout(10000),
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { D3ROError, ErrorCode } from '@d3ro/core/errors'
|
|||
import type { ISTTDriver } from '../types'
|
||||
import type { TranscriptionResult, TranscribeOptions } from '../../LocalSTTService'
|
||||
import type { STTProviderConfig } from '@d3ro/core/types'
|
||||
import { pcmToWav, createProbeWav } from '../audio-utils'
|
||||
import { pcmToWav, createProbeWav, bufferToBody } from '../audio-utils'
|
||||
import { getLogger } from '../../LoggerService'
|
||||
|
||||
const logger = getLogger('DeepgramDriver')
|
||||
|
|
@ -54,7 +54,7 @@ export class DeepgramDriver implements ISTTDriver {
|
|||
Authorization: `Token ${apiKey}`,
|
||||
'Content-Type': 'audio/wav',
|
||||
},
|
||||
body: wavBuffer,
|
||||
body: bufferToBody(wavBuffer),
|
||||
signal: AbortSignal.timeout(25000),
|
||||
})
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ export class DeepgramDriver implements ISTTDriver {
|
|||
Authorization: `Token ${apiKey}`,
|
||||
'Content-Type': 'audio/wav',
|
||||
},
|
||||
body: probe,
|
||||
body: bufferToBody(probe),
|
||||
signal: AbortSignal.timeout(10000),
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue