feat(storage): move desktop SQLite ownership into a storage utility process
Main no longer opens desktop business SQLite. A single utility-process
storage owner holds the Assistant, Knowledge, attachment, document-result,
remote-binding and native model-ledger databases and runs whole domain
transactions; Main and Runtime callers use explicit async domain operations.
- Remove the Main readWithFallback path and the supervision worker's
separate business write connection; manifests are scanned read-only and
committed by the sole writer.
- Remote events are ACKed only after commit; process loss reports
unconfirmed writes instead of replaying them.
- Use native utilityProcess messaging with one in-flight bound (32) and
ordered waiting instead of capacity rejection and retries.
- Start the storage process before the window so startup overlaps.
- Keep short tool output in memory and store full output once in owned
storage; paging, branching, restart and deletion reuse that copy.
- Put required runtime launch files under goodbuddy-prefixed owned temp
directories and reclaim confirmed stale ones; stop writing duplicate
parsed.md.
- Add lint guards for SQLite ownership and architecture, concurrency
acceptance fixtures, and maintenance/architecture documentation.
Validation: typecheck and lint pass. Alternating baseline (edf019aa) vs
candidate runs show no regression on single-session, C01, C02, C04 and a
5-minute C06; Main p99 drops from ~21 ms to ~16.7 ms under concurrency.
Fresh portable copy (1.75M events) upgrade content unchanged; real-model
output paging passes with 4 external requests. Full npm test runs had
1-9 different timing-sensitive failures per run that pass when rerun.
Not covered: C03 review publication, C07 source body, app-level C05.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { AgentExecutionRequest, AgentRuntime, RuntimeModelUsageEvent } from '../agent/runtime'
|
||||
import type {
|
||||
MagicNoteEntry,
|
||||
@@ -23,6 +23,17 @@ const entry: MagicNoteEntry = {
|
||||
}
|
||||
|
||||
describe('magic note analyzer', () => {
|
||||
it('awaits model usage persistence and rejects analysis if the write fails', async () => {
|
||||
const { runtime } = recordingRuntime()
|
||||
let reject!: (error: Error) => void
|
||||
const persist = vi.fn(() => new Promise<void>((_resolve, fail) => { reject = fail }))
|
||||
const pending = analyzeMagicNoteEntry(runtime, entry, options, undefined, persist)
|
||||
const rejected = expect(pending).rejects.toThrow('Usage write failed')
|
||||
await vi.waitFor(() => expect(persist).toHaveBeenCalledOnce())
|
||||
reject(new Error('Usage write failed'))
|
||||
await rejected
|
||||
})
|
||||
|
||||
const png = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/l9sAAAAASUVORK5CYII='
|
||||
const canvasEntry = {
|
||||
...entry,
|
||||
@@ -127,7 +138,7 @@ describe('magic note analyzer', () => {
|
||||
const usage: RuntimeModelUsageEvent[] = []
|
||||
const comments = await analyzeMagicNoteEntry(
|
||||
runtime, canvasEntry, options, undefined,
|
||||
(event) => usage.push(event), { supportsImageInput: true, canvasPageCount: 2 }
|
||||
(event) => { usage.push(event) }, { supportsImageInput: true, canvasPageCount: 2 }
|
||||
)
|
||||
expect(requests).toHaveLength(1)
|
||||
expect(requests[0]?.images).toEqual([
|
||||
|
||||
@@ -167,7 +167,7 @@ async function analyzeComments(
|
||||
},
|
||||
options: MagicNoteAnalysisOptions,
|
||||
onText?: (delta: string) => void,
|
||||
onModelUsage?: (event: RuntimeModelUsageEvent) => void
|
||||
onModelUsage?: (event: RuntimeModelUsageEvent) => void | Promise<void>
|
||||
): Promise<MagicNoteComment[]> {
|
||||
const source = input.source.trim()
|
||||
if (!source && !input.images?.length) {
|
||||
@@ -238,7 +238,7 @@ ${outputInstructions}`,
|
||||
}
|
||||
}
|
||||
} else if (event.type === 'model-usage') {
|
||||
onModelUsage?.(event)
|
||||
await onModelUsage?.(event)
|
||||
} else if (event.type === 'tool') {
|
||||
throw new Error('魔法笔记 AI 分析不允许工具调用')
|
||||
} else if (event.type === 'generated-image') {
|
||||
@@ -304,7 +304,7 @@ export async function analyzeMagicNoteEntry(
|
||||
entry: MagicNoteEntry,
|
||||
options: MagicNoteAnalysisOptions,
|
||||
onText?: (delta: string) => void,
|
||||
onModelUsage?: (event: RuntimeModelUsageEvent) => void,
|
||||
onModelUsage?: (event: RuntimeModelUsageEvent) => void | Promise<void>,
|
||||
context?: NoteAnalysisContext
|
||||
): Promise<MagicNoteComment[]> {
|
||||
const input = canvasAnalysisInput(entry.content, options, context?.supportsImageInput === true, context?.canvasPageCount)
|
||||
@@ -328,7 +328,7 @@ export async function analyzeMagicNoteDraft(
|
||||
plainText: string,
|
||||
options: MagicNoteAnalysisOptions,
|
||||
onText?: (delta: string) => void,
|
||||
onModelUsage?: (event: RuntimeModelUsageEvent) => void,
|
||||
onModelUsage?: (event: RuntimeModelUsageEvent) => void | Promise<void>,
|
||||
context?: NoteAnalysisContext
|
||||
): Promise<MagicNoteComment[]> {
|
||||
const input = canvasAnalysisInput(context?.content, options, context?.supportsImageInput === true, context?.canvasPageCount)
|
||||
@@ -352,7 +352,7 @@ export async function analyzeMagicTodo(
|
||||
todo: MagicTodoItem,
|
||||
options: MagicNoteAnalysisOptions,
|
||||
onText?: (delta: string) => void,
|
||||
onModelUsage?: (event: RuntimeModelUsageEvent) => void,
|
||||
onModelUsage?: (event: RuntimeModelUsageEvent) => void | Promise<void>,
|
||||
context?: NoteAnalysisContext
|
||||
): Promise<MagicNoteComment[]> {
|
||||
const input = canvasAnalysisInput(
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { createDefaultModelRuntime } from '../agent/create-runtime'
|
||||
import type { RuntimeModelUsageEvent } from '../agent/runtime'
|
||||
import type { ApplicationSettingsStore } from '../application-settings-store'
|
||||
import type { AssistantDatabase } from '../assistant/assistant-database'
|
||||
import type { AssistantStoragePort, Awaitable } from '../assistant-storage-port'
|
||||
import type { RuntimeSettingsStore } from '../runtime-settings-store'
|
||||
import { assertTrustedSender } from '../trusted-ipc-sender'
|
||||
import {
|
||||
@@ -21,10 +21,10 @@ import { magicNotePlainText, validateMagicNoteContent } from './rich-content'
|
||||
|
||||
type AnalysisDependencies = {
|
||||
window: BrowserWindow
|
||||
assistantDatabase: AssistantDatabase
|
||||
assistantDatabase: AssistantStoragePort
|
||||
settingsStore: Pick<RuntimeSettingsStore, 'getResolvedSettings'>
|
||||
applicationSettingsStore?: Pick<ApplicationSettingsStore, 'get'>
|
||||
persistModelUsage: (event: RuntimeModelUsageEvent) => void
|
||||
persistModelUsage: (event: RuntimeModelUsageEvent) => Awaitable<void>
|
||||
safeRuntimeError: (error: unknown, fallback: string) => string
|
||||
}
|
||||
|
||||
@@ -39,24 +39,24 @@ export function registerMagicNotesAnalysisIpcHandlers(
|
||||
const { entryId, requestId, direction, format, canvasImages, canvasPageText, expectedRevision } =
|
||||
magicNoteAnalyzeSchema.parse(input)
|
||||
const settings = await settingsStore.getResolvedSettings()
|
||||
const entry = assistantDatabase.getMagicNoteEntry(entryId)
|
||||
const entry = await assistantDatabase.getMagicNoteEntry(entryId)
|
||||
if (expectedRevision !== undefined && entry.revision !== expectedRevision) {
|
||||
throw new Error('记录已被更新,请重新捕获并分析')
|
||||
}
|
||||
const note = assistantDatabase.getMagicNoteContext(entry.noteId)
|
||||
const note = await assistantDatabase.getMagicNoteContext(entry.noteId)
|
||||
const canvasPageCount = (await applicationSettingsStore?.get())?.magicNoteCanvasPageCount ?? 1
|
||||
const analysisRuntime = createDefaultModelRuntime(
|
||||
settings.workspacePath,
|
||||
settings
|
||||
)
|
||||
assistantDatabase.createTask({
|
||||
id: requestId,
|
||||
title: `分析笔记:${note.title}`,
|
||||
instructions: '使用无工具模型对笔记记录进行只读分析',
|
||||
origin: 'assistant',
|
||||
visible: false
|
||||
})
|
||||
try {
|
||||
await assistantDatabase.createTask({
|
||||
id: requestId,
|
||||
title: `分析笔记:${note.title}`,
|
||||
instructions: '使用无工具模型对笔记记录进行只读分析',
|
||||
origin: 'assistant',
|
||||
visible: false
|
||||
})
|
||||
const comments = await analyzeMagicNoteEntry(
|
||||
analysisRuntime,
|
||||
entry,
|
||||
@@ -80,16 +80,16 @@ export function registerMagicNotesAnalysisIpcHandlers(
|
||||
persistModelUsage,
|
||||
{ supportsImageInput: settings.supportsImageInput === true, canvasPageCount }
|
||||
)
|
||||
const analyzedNote = assistantDatabase.saveMagicNoteAnalysis({
|
||||
const analyzedNote = await assistantDatabase.saveMagicNoteAnalysis({
|
||||
entryId,
|
||||
expectedRevision: entry.revision,
|
||||
comments
|
||||
})
|
||||
assistantDatabase.updateTaskStatus(requestId, 'completed')
|
||||
await assistantDatabase.updateTaskStatus(requestId, 'completed')
|
||||
return analyzedNote
|
||||
} catch (error) {
|
||||
const message = safeRuntimeError(error, '魔法笔记 AI 分析失败')
|
||||
assistantDatabase.updateTaskStatus(requestId, 'failed', message)
|
||||
await assistantDatabase.updateTaskStatus(requestId, 'failed', message)
|
||||
throw new Error(message, { cause: error })
|
||||
} finally {
|
||||
try {
|
||||
@@ -117,14 +117,14 @@ export function registerMagicNotesAnalysisIpcHandlers(
|
||||
settings
|
||||
)
|
||||
const { requestId, direction, format, canvasImages, canvasPageText } = parsed
|
||||
assistantDatabase.createTask({
|
||||
id: requestId,
|
||||
title: '分析未保存笔记草稿',
|
||||
instructions: '使用无工具模型对未保存笔记草稿进行只读分析',
|
||||
origin: 'assistant',
|
||||
visible: false
|
||||
})
|
||||
try {
|
||||
await assistantDatabase.createTask({
|
||||
id: requestId,
|
||||
title: '分析未保存笔记草稿',
|
||||
instructions: '使用无工具模型对未保存笔记草稿进行只读分析',
|
||||
origin: 'assistant',
|
||||
visible: false
|
||||
})
|
||||
const comments = await analyzeMagicNoteDraft(
|
||||
analysisRuntime,
|
||||
plainText,
|
||||
@@ -148,7 +148,7 @@ export function registerMagicNotesAnalysisIpcHandlers(
|
||||
persistModelUsage,
|
||||
{ supportsImageInput: settings.supportsImageInput === true, content, canvasPageCount }
|
||||
)
|
||||
assistantDatabase.updateTaskStatus(requestId, 'completed')
|
||||
await assistantDatabase.updateTaskStatus(requestId, 'completed')
|
||||
return {
|
||||
id: randomUUID(),
|
||||
comments,
|
||||
@@ -157,7 +157,7 @@ export function registerMagicNotesAnalysisIpcHandlers(
|
||||
}
|
||||
} catch (error) {
|
||||
const message = safeRuntimeError(error, '魔法笔记草稿 AI 分析失败')
|
||||
assistantDatabase.updateTaskStatus(requestId, 'failed', message)
|
||||
await assistantDatabase.updateTaskStatus(requestId, 'failed', message)
|
||||
throw new Error(message, { cause: error })
|
||||
} finally {
|
||||
try {
|
||||
@@ -183,9 +183,9 @@ export function registerMagicTodosAnalysisIpcHandlers(
|
||||
const { todoId, requestId, direction, format, canvasImages, canvasPageText, sourceEntryRevision } =
|
||||
magicTodoIdSchema.parse(input)
|
||||
const settings = await settingsStore.getResolvedSettings()
|
||||
const todo = assistantDatabase.getMagicTodo(todoId)
|
||||
const todo = await assistantDatabase.getMagicTodo(todoId)
|
||||
const canvasPageCount = (await applicationSettingsStore?.get())?.magicNoteCanvasPageCount ?? 1
|
||||
const entry = assistantDatabase.getMagicNoteEntry(todo.entryId)
|
||||
const entry = await assistantDatabase.getMagicNoteEntry(todo.entryId)
|
||||
if (sourceEntryRevision !== undefined && entry.revision !== sourceEntryRevision) {
|
||||
throw new Error('来源记录已被更新,请重新捕获并分析')
|
||||
}
|
||||
@@ -193,14 +193,14 @@ export function registerMagicTodosAnalysisIpcHandlers(
|
||||
settings.workspacePath,
|
||||
settings
|
||||
)
|
||||
assistantDatabase.createTask({
|
||||
id: requestId,
|
||||
title: `分析待办:${todo.title}`,
|
||||
instructions: '使用无工具模型对魔法笔记待办进行只读分析',
|
||||
origin: 'assistant',
|
||||
visible: false
|
||||
})
|
||||
try {
|
||||
await assistantDatabase.createTask({
|
||||
id: requestId,
|
||||
title: `分析待办:${todo.title}`,
|
||||
instructions: '使用无工具模型对魔法笔记待办进行只读分析',
|
||||
origin: 'assistant',
|
||||
visible: false
|
||||
})
|
||||
const comments = await analyzeMagicTodo(
|
||||
analysisRuntime,
|
||||
todo,
|
||||
@@ -224,17 +224,17 @@ export function registerMagicTodosAnalysisIpcHandlers(
|
||||
persistModelUsage,
|
||||
{ supportsImageInput: settings.supportsImageInput === true, content: entry.content, canvasPageCount }
|
||||
)
|
||||
const analyzedTodo = assistantDatabase.saveMagicTodoAnalysis({
|
||||
const analyzedTodo = await assistantDatabase.saveMagicTodoAnalysis({
|
||||
todoId,
|
||||
expectedRevision: todo.revision,
|
||||
sourceEntryRevision: entry.revision,
|
||||
comments
|
||||
})
|
||||
assistantDatabase.updateTaskStatus(requestId, 'completed')
|
||||
await assistantDatabase.updateTaskStatus(requestId, 'completed')
|
||||
return analyzedTodo
|
||||
} catch (error) {
|
||||
const message = safeRuntimeError(error, '魔法笔记待办 AI 分析失败')
|
||||
assistantDatabase.updateTaskStatus(requestId, 'failed', message)
|
||||
await assistantDatabase.updateTaskStatus(requestId, 'failed', message)
|
||||
throw new Error(message, { cause: error })
|
||||
} finally {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import type { BrowserWindow, IpcMainInvokeEvent, ipcMain } from 'electron'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { AssistantStoragePort } from '../assistant-storage-port'
|
||||
import { ipcChannels } from '../../shared/ipc-channels'
|
||||
import { registerMagicNotesIpcHandlers, registerMagicTodosIpcHandlers } from './magic-notes-ipc'
|
||||
|
||||
function fixture(database: Partial<AssistantStoragePort>) {
|
||||
const handlers = new Map<string, Parameters<typeof ipcMain.handle>[1]>()
|
||||
const register: typeof ipcMain.handle = (channel, handler) => { handlers.set(channel, handler) }
|
||||
const webContents = { mainFrame: { url: 'file:///app.html' }, getURL: () => 'file:///app.html' }
|
||||
const window = { webContents } as unknown as BrowserWindow
|
||||
const event = { sender: webContents, senderFrame: webContents.mainFrame } as unknown as IpcMainInvokeEvent
|
||||
registerMagicNotesIpcHandlers(register, window, database as AssistantStoragePort)
|
||||
registerMagicTodosIpcHandlers(register, window, database as AssistantStoragePort)
|
||||
return { invoke: (channel: string, input?: unknown, sender = event) => handlers.get(channel)!(sender, input) }
|
||||
}
|
||||
|
||||
describe('async magic notes IPC', () => {
|
||||
it('resolves list DTOs and preserves sender/schema checks before storage', async () => {
|
||||
const listMagicNotes = vi.fn(async () => [])
|
||||
const createMagicNote = vi.fn()
|
||||
const f = fixture({ listMagicNotes, listMagicNoteTags: async () => [], createMagicNote })
|
||||
await expect(f.invoke(ipcChannels.magicNotesList)).resolves.toEqual({ notes: [], tags: [] })
|
||||
await expect(f.invoke(ipcChannels.magicNotesList, undefined, {} as IpcMainInvokeEvent)).rejects.toThrow('IPC')
|
||||
expect(listMagicNotes).toHaveBeenCalledOnce()
|
||||
await expect(f.invoke(ipcChannels.magicNotesCreate, {})).rejects.toThrow()
|
||||
expect(createMagicNote).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not acknowledge deletion or read a todo note before its write commits', async () => {
|
||||
let commit!: () => void
|
||||
const deletion = new Promise<void>(resolve => { commit = resolve })
|
||||
const f = fixture({ deleteMagicNote: () => deletion })
|
||||
const pending = f.invoke(ipcChannels.magicNotesDelete, { noteId: '00000000-0000-4000-8000-000000000001' })
|
||||
expect(pending).toBe(deletion)
|
||||
commit()
|
||||
await pending
|
||||
|
||||
const getMagicNote = vi.fn(async () => ({ id: 'note' }))
|
||||
let save!: (value: unknown) => void
|
||||
const updated = new Promise(resolve => { save = resolve })
|
||||
const todos = fixture({ updateMagicTodo: (() => updated) as AssistantStoragePort['updateMagicTodo'], getMagicNote: getMagicNote as never })
|
||||
const result = todos.invoke(ipcChannels.magicTodosUpdate, { todoId: '00000000-0000-4000-8000-000000000002', completed: true, expectedRevision: 0 })
|
||||
await Promise.resolve()
|
||||
expect(getMagicNote).not.toHaveBeenCalled()
|
||||
save({ noteId: 'note' })
|
||||
await expect(result).resolves.toEqual({ todo: { noteId: 'note' }, note: { id: 'note' } })
|
||||
})
|
||||
})
|
||||
@@ -13,18 +13,18 @@ import {
|
||||
magicNoteUpdateSchema,
|
||||
magicTodoUpdateSchema
|
||||
} from '../../shared/magic-notes-contracts'
|
||||
import type { AssistantDatabase } from '../assistant/assistant-database'
|
||||
import type { AssistantStoragePort } from '../assistant-storage-port'
|
||||
import { assertTrustedSender } from '../trusted-ipc-sender'
|
||||
import { magicNotePlainText, validateMagicNoteContent } from './rich-content'
|
||||
|
||||
export function registerMagicNotesIpcHandlers(
|
||||
registerHandler: typeof ipcMain.handle,
|
||||
window: BrowserWindow,
|
||||
assistantDatabase: AssistantDatabase
|
||||
assistantDatabase: AssistantStoragePort
|
||||
): void {
|
||||
const resolveMagicNoteSource = (source?: MagicNoteSource): MagicNoteSource | undefined => {
|
||||
const resolveMagicNoteSource = async (source?: MagicNoteSource): Promise<MagicNoteSource | undefined> => {
|
||||
if (!source) return undefined
|
||||
const conversation = assistantDatabase.getConversation(source.conversationId)
|
||||
const conversation = await assistantDatabase.getConversation(source.conversationId)
|
||||
const messages = new Map(conversation.messages.map((message) => [message.id, message]))
|
||||
for (const id of source.messageIds) {
|
||||
const message = messages.get(id)
|
||||
@@ -32,7 +32,7 @@ export function registerMagicNotesIpcHandlers(
|
||||
throw new Error('Invalid conversation source message')
|
||||
}
|
||||
}
|
||||
const project = conversation.projectId ? assistantDatabase.getProject(conversation.projectId) : undefined
|
||||
const project = conversation.projectId ? await assistantDatabase.getProject(conversation.projectId) : undefined
|
||||
return {
|
||||
kind: source.kind,
|
||||
conversationId: conversation.id,
|
||||
@@ -49,11 +49,11 @@ export function registerMagicNotesIpcHandlers(
|
||||
return assistantDatabase.searchMagicNoteSummaries(query, limit)
|
||||
})
|
||||
|
||||
registerHandler(ipcChannels.magicNotesList, (event) => {
|
||||
registerHandler(ipcChannels.magicNotesList, async (event) => {
|
||||
assertTrustedSender(event, window)
|
||||
return {
|
||||
notes: assistantDatabase.listMagicNotes(),
|
||||
tags: assistantDatabase.listMagicNoteTags()
|
||||
notes: await assistantDatabase.listMagicNotes(),
|
||||
tags: await assistantDatabase.listMagicNoteTags()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -65,7 +65,7 @@ export function registerMagicNotesIpcHandlers(
|
||||
registerHandler(ipcChannels.magicNotesDeleteTag, (event, input: unknown) => {
|
||||
assertTrustedSender(event, window)
|
||||
const { tagId } = magicNoteTagDeleteSchema.parse(input)
|
||||
assistantDatabase.deleteMagicNoteTag(tagId)
|
||||
return assistantDatabase.deleteMagicNoteTag(tagId)
|
||||
})
|
||||
|
||||
registerHandler(ipcChannels.magicNotesGet, (event, input: unknown) => {
|
||||
@@ -74,10 +74,10 @@ export function registerMagicNotesIpcHandlers(
|
||||
return assistantDatabase.getMagicNote(noteId)
|
||||
})
|
||||
|
||||
registerHandler(ipcChannels.magicNotesCreate, (event, input: unknown) => {
|
||||
registerHandler(ipcChannels.magicNotesCreate, async (event, input: unknown) => {
|
||||
assertTrustedSender(event, window)
|
||||
const parsed = magicNoteCreateSchema.parse(input)
|
||||
return assistantDatabase.createMagicNote({ ...parsed, source: resolveMagicNoteSource(parsed.source) })
|
||||
return assistantDatabase.createMagicNote({ ...parsed, source: await resolveMagicNoteSource(parsed.source) })
|
||||
})
|
||||
|
||||
registerHandler(ipcChannels.magicNotesUpdate, (event, input: unknown) => {
|
||||
@@ -90,19 +90,19 @@ export function registerMagicNotesIpcHandlers(
|
||||
registerHandler(ipcChannels.magicNotesDelete, (event, input: unknown) => {
|
||||
assertTrustedSender(event, window)
|
||||
const { noteId } = magicNoteDeleteSchema.parse(input)
|
||||
assistantDatabase.deleteMagicNote(noteId)
|
||||
return assistantDatabase.deleteMagicNote(noteId)
|
||||
})
|
||||
|
||||
registerHandler(
|
||||
ipcChannels.magicNotesCreateEntry,
|
||||
(event, input: unknown) => {
|
||||
async (event, input: unknown) => {
|
||||
assertTrustedSender(event, window)
|
||||
const parsed = magicNoteEntryCreateSchema.parse(input)
|
||||
const content = validateMagicNoteContent(parsed.content)
|
||||
return assistantDatabase.createMagicNoteEntry({
|
||||
noteId: parsed.noteId,
|
||||
content,
|
||||
source: resolveMagicNoteSource(parsed.source),
|
||||
source: await resolveMagicNoteSource(parsed.source),
|
||||
plainText: magicNotePlainText(content)
|
||||
})
|
||||
}
|
||||
@@ -136,13 +136,13 @@ export function registerMagicNotesIpcHandlers(
|
||||
export function registerMagicTodosIpcHandlers(
|
||||
registerHandler: typeof ipcMain.handle,
|
||||
window: BrowserWindow,
|
||||
assistantDatabase: AssistantDatabase
|
||||
assistantDatabase: AssistantStoragePort
|
||||
): void {
|
||||
registerHandler(
|
||||
ipcChannels.magicTodosList,
|
||||
(event) => {
|
||||
async (event) => {
|
||||
assertTrustedSender(event, window)
|
||||
return { todos: assistantDatabase.listMagicTodos() }
|
||||
return { todos: await assistantDatabase.listMagicTodos() }
|
||||
}
|
||||
)
|
||||
|
||||
@@ -156,14 +156,14 @@ export function registerMagicTodosIpcHandlers(
|
||||
|
||||
registerHandler(
|
||||
ipcChannels.magicTodosUpdate,
|
||||
(event, input: unknown) => {
|
||||
async (event, input: unknown) => {
|
||||
assertTrustedSender(event, window)
|
||||
const todo = assistantDatabase.updateMagicTodo(
|
||||
const todo = await assistantDatabase.updateMagicTodo(
|
||||
magicTodoUpdateSchema.parse(input)
|
||||
)
|
||||
return {
|
||||
todo,
|
||||
note: assistantDatabase.getMagicNote(todo.noteId)
|
||||
note: await assistantDatabase.getMagicNote(todo.noteId)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user