v0.7.0: 过程/答案分离 + 流式时序 + 自定义 API + 虚拟列表 + 打包

This commit is contained in:
Rick Ye
2026-09-06 23:15:41 +08:00
parent 5799be543b
commit f8c785afbc
15 changed files with 4526 additions and 365 deletions
+62 -4
View File
@@ -1,4 +1,5 @@
import { BrowserWindow, dialog, ipcMain } from 'electron'
import { BrowserWindow, dialog, ipcMain, shell } from 'electron'
import { mkdirSync } from 'node:fs'
import { userInfo } from 'node:os'
import { AgentService } from './pi/agent-service'
@@ -90,6 +91,37 @@ export function registerIpc(): AgentService {
ipcMain.handle('models:catalog', () => pass(() => service.getModelCatalog()))
// ---- 自定义 API 提供商(models.json)----
ipcMain.handle('models:custom:list', () => service.listCustomProviders())
ipcMain.handle('models:custom:save', (_event, config: unknown) => {
if (typeof config !== 'object' || config === null) throw new Error('invalid custom provider')
const c = config as Record<string, unknown>
if (
typeof c.id !== 'string' ||
typeof c.baseUrl !== 'string' ||
typeof c.apiKey !== 'string' ||
typeof c.api !== 'string' ||
!Array.isArray(c.models)
) {
throw new Error('invalid custom provider')
}
const normalized = {
id: c.id,
name: typeof c.name === 'string' ? c.name : c.id,
baseUrl: c.baseUrl,
apiKey: c.apiKey,
api: c.api,
models: c.models.filter((m): m is string => typeof m === 'string')
}
return pass(() => service.saveCustomProvider(normalized))
})
ipcMain.handle('models:custom:delete', (_event, id: unknown) => {
if (typeof id !== 'string' || !id.trim()) throw new Error('invalid provider id')
return pass(() => service.deleteCustomProvider(id))
})
ipcMain.handle('models:set', (_event, providerId: unknown, modelId: unknown) => {
if (typeof providerId !== 'string' || typeof modelId !== 'string') throw new Error('invalid model')
return pass(() => service.setModel(providerId, modelId))
@@ -178,9 +210,35 @@ export function registerIpc(): AgentService {
})
ipcMain.handle('permissions:set', (_event, mode: unknown) => {
const valid: ReadonlySet<string> = new Set(['confirm', 'autoEdit', 'plan', 'full'])
if (typeof mode !== 'string' || !valid.has(mode)) throw new Error('invalid permission mode')
service.setPermissionMode(mode as never)
if (mode !== 'confirm' && mode !== 'full') throw new Error('invalid permission mode')
service.setPermissionMode(mode)
})
// ---- 设置页:技能 / 命令详情 ----
ipcMain.handle('resources:details', () =>
service.listResourceDetails().catch((err: unknown) => {
throw new Error(err instanceof Error ? err.message : String(err))
})
)
// 在系统文件管理器中打开(技能/命令来源文件所在目录)
ipcMain.handle('shell:openPath', (_event, target: unknown) => {
if (typeof target !== 'string' || !target.trim()) throw new Error('invalid path')
return shell.openPath(target)
})
// 打开资源目录:不存在时先创建(skills/prompts 目录通常尚未建立)
ipcMain.handle('shell:openDirectory', (_event, target: unknown) => {
if (typeof target !== 'string' || !target.trim()) throw new Error('invalid path')
try {
mkdirSync(target, { recursive: true })
} catch {
// 已存在等情况忽略,交给 openPath 反馈
}
return shell.openPath(target).then((result) => {
// shell.openPath 失败时以字符串返回错误信息,成功返回空串
if (result) throw new Error(result)
})
})
ipcMain.handle('files:list', () =>