180 lines
9.9 KiB
TypeScript
180 lines
9.9 KiB
TypeScript
/**
|
||
* Preload:contextBridge 暴露受控 API。
|
||
* 渲染进程只能看到这里列出的方法,拿不到 ipcRenderer 本体。
|
||
*/
|
||
|
||
import { contextBridge, ipcRenderer } from 'electron'
|
||
import type { ChatEvent, WindowState } from '@shared/types'
|
||
import type { ConfigPatchLike, EditAction, SendPayload, WorkspaceAttachResult, YCodeApi } from '@shared/ipc'
|
||
import type { DownloadPayload, ResourceRange } from '@shared/resources'
|
||
import type { InteractionResponse } from '@shared/conversation'
|
||
|
||
const api: YCodeApi = {
|
||
experts: {
|
||
list: () => ipcRenderer.invoke('experts:list'),
|
||
read: id => ipcRenderer.invoke('experts:read', id),
|
||
activate: id => ipcRenderer.invoke('experts:activate', id),
|
||
remove: id => ipcRenderer.invoke('experts:remove', id),
|
||
openPath: id => ipcRenderer.invoke('experts:open-path', id)
|
||
},
|
||
skills: {
|
||
list: (cwd) => ipcRenderer.invoke('skills:list', cwd),
|
||
importPath: (path, overwrite, kind) => ipcRenderer.invoke('skills:import', path, overwrite, kind),
|
||
read: (id, cwd) => ipcRenderer.invoke('skills:read', id, cwd),
|
||
setEnabled: (id, enabled, cwd) => ipcRenderer.invoke('skills:enable', id, enabled, cwd),
|
||
remove: (id) => ipcRenderer.invoke('skills:remove', id),
|
||
export: (id, destination) => ipcRenderer.invoke('skills:export', id, destination)
|
||
},
|
||
mcp: {
|
||
list: () => ipcRenderer.invoke('mcp:list'),
|
||
save: (input) => ipcRenderer.invoke('mcp:save', input),
|
||
trust: (id, hash) => ipcRenderer.invoke('mcp:trust', id, hash),
|
||
connect: (id) => ipcRenderer.invoke('mcp:connect', id),
|
||
disconnect: (id) => ipcRenderer.invoke('mcp:disconnect', id),
|
||
setEnabled: (id, enabled) => ipcRenderer.invoke('mcp:enable', id, enabled),
|
||
setToolEnabled: (id, tool, enabled) => ipcRenderer.invoke('mcp:tool-enable', id, tool, enabled),
|
||
remove: (id) => ipcRenderer.invoke('mcp:remove', id)
|
||
},
|
||
terminal: {
|
||
rebind: (id, previousSessionId, nextSessionId) => ipcRenderer.invoke('terminal:rebind', id, previousSessionId, nextSessionId),
|
||
create: (sessionId, cols, rows) => ipcRenderer.invoke('terminal:create', sessionId, cols, rows),
|
||
get: (id, sessionId) => ipcRenderer.invoke('terminal:get', id, sessionId),
|
||
write: (id, data, sessionId) => ipcRenderer.invoke('terminal:write', id, data, sessionId),
|
||
resize: (id, cols, rows, sessionId) => ipcRenderer.invoke('terminal:resize', id, cols, rows, sessionId),
|
||
clear: (id, sessionId) => ipcRenderer.invoke('terminal:clear', id, sessionId),
|
||
close: (id, sessionId) => ipcRenderer.invoke('terminal:close', id, sessionId),
|
||
onEvent: (listener) => {
|
||
const handler = (_event: unknown, payload: Parameters<typeof listener>[0]): void => listener(payload)
|
||
ipcRenderer.on('terminal:event', handler)
|
||
return () => ipcRenderer.removeListener('terminal:event', handler)
|
||
}
|
||
},
|
||
browser: {
|
||
rebind: (id, previousSessionId, nextSessionId) => ipcRenderer.invoke('browser:rebind', id, previousSessionId, nextSessionId),
|
||
create: (sessionId, url) => ipcRenderer.invoke('browser:create', sessionId, url),
|
||
get: (id, sessionId) => ipcRenderer.invoke('browser:get', id, sessionId),
|
||
navigate: (id, url, sessionId) => ipcRenderer.invoke('browser:navigate', id, url, sessionId),
|
||
back: (id, sessionId) => ipcRenderer.invoke('browser:back', id, sessionId),
|
||
forward: (id, sessionId) => ipcRenderer.invoke('browser:forward', id, sessionId),
|
||
reload: (id, sessionId) => ipcRenderer.invoke('browser:reload', id, sessionId),
|
||
bounds: (id, bounds, sessionId) => ipcRenderer.invoke('browser:bounds', id, bounds, sessionId),
|
||
close: (id, sessionId) => ipcRenderer.invoke('browser:close', id, sessionId)
|
||
},
|
||
window: {
|
||
minimize: () => ipcRenderer.invoke('window:minimize'),
|
||
toggleMaximize: () => ipcRenderer.invoke('window:toggleMaximize'),
|
||
close: () => ipcRenderer.invoke('window:close'),
|
||
getState: () => ipcRenderer.invoke('window:state'),
|
||
onState: (listener: (state: WindowState) => void) => {
|
||
const handler = (_event: unknown, payload: WindowState): void => listener(payload)
|
||
ipcRenderer.on('window:state', handler)
|
||
return () => {
|
||
ipcRenderer.removeListener('window:state', handler)
|
||
}
|
||
},
|
||
onFocus: (listener: (focused: boolean) => void) => {
|
||
const handler = (_event: unknown, payload: { focused: boolean }): void =>
|
||
listener(payload.focused)
|
||
ipcRenderer.on('window:focus', handler)
|
||
return () => {
|
||
ipcRenderer.removeListener('window:focus', handler)
|
||
}
|
||
}
|
||
},
|
||
edit: {
|
||
action: (action: EditAction) => ipcRenderer.invoke('edit:action', action)
|
||
},
|
||
app: {
|
||
info: () => ipcRenderer.invoke('app:info'),
|
||
openConfigDir: () => ipcRenderer.invoke('app:openConfigDir'),
|
||
openSettings: (section, action) => ipcRenderer.invoke('app:openSettings', section, action),
|
||
onSettingsNavigate: (listener) => {
|
||
const handler = (_event: Electron.IpcRendererEvent, section: string, action?: 'new'): void => listener(section, action)
|
||
ipcRenderer.on('settings:navigate', handler)
|
||
return () => { ipcRenderer.removeListener('settings:navigate', handler) }
|
||
},
|
||
onSettingsChanged: (listener) => {
|
||
const handler = (): void => listener()
|
||
ipcRenderer.on('settings:changed', handler)
|
||
return () => ipcRenderer.removeListener('settings:changed', handler)
|
||
}
|
||
},
|
||
git: {
|
||
branch: (sessionId) => ipcRenderer.invoke('git:branch', sessionId)
|
||
},
|
||
config: {
|
||
get: () => ipcRenderer.invoke('config:get'),
|
||
update: (patch: ConfigPatchLike) => ipcRenderer.invoke('config:update', patch)
|
||
},
|
||
general: {
|
||
get: (options) => ipcRenderer.invoke('general:get', options),
|
||
update: (patch) => ipcRenderer.invoke('general:update', patch),
|
||
openFolder: (kind) => ipcRenderer.invoke('general:openFolder', kind),
|
||
changeWorkspaceRoot: (path) => ipcRenderer.invoke('general:changeWorkspaceRoot', path),
|
||
openSystemNotificationSettings: () => ipcRenderer.invoke('general:open-system-notification-settings')
|
||
},
|
||
workspace: {
|
||
get: () => ipcRenderer.invoke('workspace:get'),
|
||
choose: () => ipcRenderer.invoke('workspace:choose'),
|
||
list: () => ipcRenderer.invoke('workspace:list'),
|
||
generate: (name) => ipcRenderer.invoke('workspace:generate', name),
|
||
attach: (path): Promise<WorkspaceAttachResult> => ipcRenderer.invoke('workspace:attach', path),
|
||
select: (id) => ipcRenderer.invoke('workspace:select', id),
|
||
rename: (id, name) => ipcRenderer.invoke('workspace:rename', id, name),
|
||
remove: (id) => ipcRenderer.invoke('workspace:remove', id),
|
||
open: (id) => ipcRenderer.invoke('workspace:open', id)
|
||
},
|
||
models: {
|
||
list: () => ipcRenderer.invoke('models:list'),
|
||
save: (input, overwrite) => ipcRenderer.invoke('models:save', input, overwrite),
|
||
remove: (id) => ipcRenderer.invoke('models:remove', id),
|
||
select: (id) => ipcRenderer.invoke('models:select', id),
|
||
test: (input) => ipcRenderer.invoke('models:test', input)
|
||
},
|
||
resources: {
|
||
list: (sessionId?: string, workspaceId?: string) => ipcRenderer.invoke('resources:list', sessionId, workspaceId),
|
||
openExternal: (url) => ipcRenderer.invoke('resources:openExternal', url),
|
||
read: (path: string, range?: ResourceRange, sessionId?: string) => ipcRenderer.invoke('resources:read', path, range, sessionId),
|
||
readText: (path: string, sessionId?: string) => ipcRenderer.invoke('resources:read-text', path, sessionId),
|
||
classify: (path: string, sessionId?: string) => ipcRenderer.invoke('resources:classify', path, sessionId),
|
||
resolveImage: (source: string, sessionId?: string) => ipcRenderer.invoke('resources:resolveImage', source, sessionId),
|
||
download: (payload: DownloadPayload) => ipcRenderer.invoke('resources:download', payload),
|
||
downloadImage: (source: string, filename?: string, sessionId?: string) => ipcRenderer.invoke('resources:downloadImage', source, filename, sessionId)
|
||
},
|
||
attachments: {
|
||
pick: () => ipcRenderer.invoke('attachments:pick'),
|
||
stage: (payload) => ipcRenderer.invoke('attachments:stage', payload),
|
||
remove: (id) => ipcRenderer.invoke('attachments:remove', id)
|
||
},
|
||
speech: { transcribe: (payload) => ipcRenderer.invoke('speech:transcribe', payload) },
|
||
session: {
|
||
list: () => ipcRenderer.invoke('session:list'),
|
||
listArchived: () => ipcRenderer.invoke('session:list-archived'),
|
||
create: (title, context) => ipcRenderer.invoke('session:create', title, context),
|
||
updateContext: (id, context) => ipcRenderer.invoke('session:update-context', id, context),
|
||
messages: (sessionId: string) => ipcRenderer.invoke('session:messages', sessionId),
|
||
rename: (sessionId: string, title: string) => ipcRenderer.invoke('session:rename', sessionId, title),
|
||
remove: (sessionId: string) => ipcRenderer.invoke('session:delete', sessionId),
|
||
setPinned: (sessionId: string, pinned: boolean) => ipcRenderer.invoke('session:set-pinned', sessionId, pinned),
|
||
setArchived: (sessionId: string, archived: boolean) => ipcRenderer.invoke('session:set-archived', sessionId, archived),
|
||
openFolder: (sessionId: string) => ipcRenderer.invoke('session:open-folder', sessionId),
|
||
openWindow: (sessionId: string) => ipcRenderer.invoke('session:open-window', sessionId)
|
||
},
|
||
chat: {
|
||
revert: (sessionId, userMessageId) => ipcRenderer.invoke('chat:revert', sessionId, userMessageId),
|
||
feedback: (sessionId, messageId, value) => ipcRenderer.invoke('chat:feedback', sessionId, messageId, value),
|
||
send: (payload: SendPayload) => ipcRenderer.invoke('chat:send', payload),
|
||
cancel: (runId: string) => ipcRenderer.invoke('chat:cancel', runId),
|
||
respond: (runId: string, interactionId: string, response: InteractionResponse) => ipcRenderer.invoke('chat:respond', runId, interactionId, response),
|
||
onEvent: (listener: (event: ChatEvent) => void) => {
|
||
const handler = (_event: unknown, payload: ChatEvent): void => listener(payload)
|
||
ipcRenderer.on('chat:event', handler)
|
||
return () => {
|
||
ipcRenderer.removeListener('chat:event', handler)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
contextBridge.exposeInMainWorld('ycode', api)
|