feat: add computer control and managed browser

This commit is contained in:
lofyer
2026-08-05 12:55:24 +08:00
parent 2f549387a6
commit 38ac2206f2
92 changed files with 21028 additions and 766 deletions
+73 -3
View File
@@ -6,6 +6,7 @@ import {
type AgentRuntimeDetection,
type AgentRuntimeStatus,
type AppInfo,
type BrowserLiveState,
type ContextAttachment,
type DesktopApi,
type KnowledgeLibrary,
@@ -17,7 +18,11 @@ import {
} from '../shared/contracts'
import { ipcChannels } from '../shared/ipc-channels'
import type {
BrowserProfileCreateInput,
BrowserProfileRenameInput,
CapabilityDiagnosticReport,
CapabilitySnapshot,
ComputerCapabilityId,
McpServerTestResult
} from '../shared/capability-contracts'
import type {
@@ -40,7 +45,8 @@ import type {
ScheduleCreateInput,
HeartbeatCreateInput,
HeartbeatUpdateInput,
ExpertCreateInput
ExpertCreateInput,
ExpertUpdateInput
} from '../shared/assistant-contracts'
const desktopApi: DesktopApi = {
@@ -116,6 +122,23 @@ const desktopApi: DesktopApi = {
return () => ipcRenderer.removeListener(ipcChannels.agentEvent, handler)
}
},
browser: {
stop: async (conversationId: string) => {
await ipcRenderer.invoke(
ipcChannels.browserStop,
{ conversationId }
)
},
onState: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
payload: BrowserLiveState
): void => listener(payload)
ipcRenderer.on(ipcChannels.browserState, handler)
return () =>
ipcRenderer.removeListener(ipcChannels.browserState, handler)
}
},
settings: {
getRuntime: () =>
ipcRenderer.invoke(
@@ -329,7 +352,15 @@ const desktopApi: DesktopApi = {
ipcRenderer.invoke(
ipcChannels.expertsCreate,
input
) as Promise<AssistantExpert>
) as Promise<AssistantExpert>,
update: (expertId: string, input: ExpertUpdateInput) =>
ipcRenderer.invoke(ipcChannels.expertsUpdate, {
expertId,
input
}) as Promise<AssistantExpert>,
remove: async (expertId: string) => {
await ipcRenderer.invoke(ipcChannels.expertsRemove, expertId)
}
},
capabilities: {
getSnapshot: () =>
@@ -369,7 +400,46 @@ const desktopApi: DesktopApi = {
ipcRenderer.invoke(
ipcChannels.capabilitiesTestMcp,
serverId
) as Promise<McpServerTestResult>
) as Promise<McpServerTestResult>,
setComputerCapabilityEnabled: (
capabilityId: ComputerCapabilityId,
enabled: boolean
) =>
ipcRenderer.invoke(ipcChannels.capabilitiesToggleComputer, {
capabilityId,
enabled
}) as Promise<CapabilitySnapshot>,
setComputerCapabilityBrowserProfile: (
capabilityId: ComputerCapabilityId,
browserProfileId: string | null
) =>
ipcRenderer.invoke(ipcChannels.capabilitiesConfigureComputer, {
capabilityId,
browserProfileId
}) as Promise<CapabilitySnapshot>,
diagnoseComputerCapability: (capabilityId: ComputerCapabilityId) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesDiagnoseComputer,
capabilityId
) as Promise<CapabilityDiagnosticReport>,
createBrowserProfile: (input: BrowserProfileCreateInput) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesCreateBrowserProfile,
input
) as Promise<CapabilitySnapshot>,
renameBrowserProfile: (input: BrowserProfileRenameInput) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesRenameBrowserProfile,
input
) as Promise<CapabilitySnapshot>,
setDefaultBrowserProfile: (profileId: string) =>
ipcRenderer.invoke(ipcChannels.capabilitiesDefaultBrowserProfile, {
profileId
}) as Promise<CapabilitySnapshot>,
removeBrowserProfile: (profileId: string) =>
ipcRenderer.invoke(ipcChannels.capabilitiesRemoveBrowserProfile, {
profileId
}) as Promise<CapabilitySnapshot>
},
context: {
selectFiles: () =>
+25
View File
@@ -11,4 +11,29 @@ describe('sandboxed preload', () => {
expect(source).not.toMatch(/\bfrom\s+['"]node:/u)
expect(source).not.toMatch(/\brequire\(\s*['"]node:/u)
})
it('does not load runtime schema libraries in the Electron sandbox', () => {
const source = readFileSync(
join(process.cwd(), 'src', 'preload', 'index.ts'),
'utf8'
)
expect(source).not.toMatch(/\b\w+Schema\b/u)
expect(source).not.toMatch(/\bfrom\s+['"]zod['"]/u)
})
it('exposes only explicit computer capability and managed profile IPC methods', () => {
const source = readFileSync(
join(process.cwd(), 'src', 'preload', 'index.ts'),
'utf8'
)
expect(source).toContain('setComputerCapabilityEnabled:')
expect(source).toContain('diagnoseComputerCapability:')
expect(source).toContain('createBrowserProfile:')
expect(source).toContain('renameBrowserProfile:')
expect(source).toContain('setDefaultBrowserProfile:')
expect(source).toContain('removeBrowserProfile:')
expect(source).not.toMatch(
/(?:setComputerCapability|BrowserProfile).{0,80}(?:executablePath|command|env|args)/su
)
})
})