feat: add app management, local inference controls, and task statistics

This commit is contained in:
mesalogo
2026-09-18 08:20:45 +08:00
parent 6dd6297414
commit bbae893c45
103 changed files with 7962 additions and 1490 deletions
+21
View File
@@ -0,0 +1,21 @@
import { expect, it, vi } from 'vitest'
import type { DesktopApi } from '../shared/contracts'
import { ipcChannels } from '../shared/ipc-channels'
const electron = vi.hoisted(() => ({
contextBridge: { exposeInMainWorld: vi.fn() },
ipcRenderer: { invoke: vi.fn(), on: vi.fn(), removeListener: vi.fn() },
webUtils: {}
}))
vi.mock('electron', () => electron)
it('reads execution statistics through the explicit typed bridge', async () => {
await import('./index')
const api = electron.contextBridge.exposeInMainWorld.mock.calls[0]![1] as DesktopApi
const summary = { durationMs: 1000, requestCount: 1, incompleteRequestCount: 0, activeRequestCount: 0, asOf: 2000, taskDurations: [{ id: 'task', durationMs: 1000, incompleteRequestCount: 0 }] }
electron.ipcRenderer.invoke.mockResolvedValue(summary)
for (const scope of [{ conversationId: 'conversation' }, { projectId: 'project' }]) {
expect(await api.tasks.getExecutionStats(scope)).toBe(summary)
expect(electron.ipcRenderer.invoke).toHaveBeenLastCalledWith(ipcChannels.tasksExecutionStats, scope)
}
})
+17
View File
@@ -65,6 +65,7 @@ import type {
AssistantHeartbeatRun,
AssistantExpert,
AssistantTask,
ExecutionStats,
ActivityHistorySnapshot,
ActivityRecord,
TokenUsageSummary,
@@ -674,6 +675,14 @@ const desktopApi: DesktopApi = {
}
},
updates: {
onSettingsChanged: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
settings: ApplicationSettings
): void => listener(settings)
ipcRenderer.on(ipcChannels.applicationSettingsChanged, handler)
return () => ipcRenderer.removeListener(ipcChannels.applicationSettingsChanged, handler)
},
getSettings: () =>
ipcRenderer.invoke(
ipcChannels.applicationSettingsGet
@@ -779,6 +788,12 @@ const desktopApi: DesktopApi = {
)
}
},
localInference: {
openSettings: () => ipcRenderer.invoke(ipcChannels.localInferenceOpenSettings),
getSnapshot: () => ipcRenderer.invoke(ipcChannels.localInferenceGet),
act: (input) => ipcRenderer.invoke(ipcChannels.localInferenceAct, input),
cancel: (taskId) => ipcRenderer.invoke(ipcChannels.localInferenceCancel, { taskId })
},
speechModels: {
getSnapshot: () =>
ipcRenderer.invoke(
@@ -1216,6 +1231,8 @@ const desktopApi: DesktopApi = {
tasks: {
list: () =>
ipcRenderer.invoke(ipcChannels.tasksList) as Promise<AssistantTask[]>,
getExecutionStats: (input) =>
ipcRenderer.invoke(ipcChannels.tasksExecutionStats, input) as Promise<ExecutionStats>,
setStatus: async (
taskId: string,
status: 'completed' | 'cancelled'