mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 16:02:50 +00:00
Add reset capability for computer-use permissions
New `computer.permissionsReset` RPC method allows resetting all permissions to not-granted state. Includes underlying platform support, web client integration, and feature interaction tracking.
This commit is contained in:
@@ -1,11 +1,16 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { handleMock, openComputerUsePermissionsMock, getComputerUsePermissionStatusMock } =
|
||||
vi.hoisted(() => ({
|
||||
handleMock: vi.fn(),
|
||||
openComputerUsePermissionsMock: vi.fn(),
|
||||
getComputerUsePermissionStatusMock: vi.fn()
|
||||
}))
|
||||
const {
|
||||
handleMock,
|
||||
openComputerUsePermissionsMock,
|
||||
getComputerUsePermissionStatusMock,
|
||||
resetComputerUsePermissionsMock
|
||||
} = vi.hoisted(() => ({
|
||||
handleMock: vi.fn(),
|
||||
openComputerUsePermissionsMock: vi.fn(),
|
||||
getComputerUsePermissionStatusMock: vi.fn(),
|
||||
resetComputerUsePermissionsMock: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('electron', () => ({
|
||||
ipcMain: {
|
||||
@@ -15,7 +20,8 @@ vi.mock('electron', () => ({
|
||||
|
||||
vi.mock('../computer/macos-computer-use-permissions', () => ({
|
||||
getComputerUsePermissionStatus: getComputerUsePermissionStatusMock,
|
||||
openComputerUsePermissions: openComputerUsePermissionsMock
|
||||
openComputerUsePermissions: openComputerUsePermissionsMock,
|
||||
resetComputerUsePermissions: resetComputerUsePermissionsMock
|
||||
}))
|
||||
|
||||
import { registerComputerUsePermissionHandlers } from './computer-use-permissions'
|
||||
@@ -25,6 +31,7 @@ describe('registerComputerUsePermissionHandlers', () => {
|
||||
handleMock.mockReset()
|
||||
getComputerUsePermissionStatusMock.mockReset()
|
||||
openComputerUsePermissionsMock.mockReset()
|
||||
resetComputerUsePermissionsMock.mockReset()
|
||||
})
|
||||
|
||||
it('launches the computer-use helper setup', async () => {
|
||||
@@ -68,4 +75,28 @@ describe('registerComputerUsePermissionHandlers', () => {
|
||||
await expect(registration![1]()).resolves.toBe(result)
|
||||
expect(getComputerUsePermissionStatusMock).toHaveBeenCalledWith()
|
||||
})
|
||||
|
||||
it('resets all computer-use permissions', async () => {
|
||||
const result = {
|
||||
platform: 'darwin',
|
||||
helperAppPath: '/Applications/Orca Computer Use.app',
|
||||
helperUnavailableReason: null,
|
||||
bundleId: 'com.stablyai.orca.computer-use',
|
||||
permissions: [
|
||||
{ id: 'accessibility', status: 'not-granted' },
|
||||
{ id: 'screenshots', status: 'not-granted' }
|
||||
]
|
||||
}
|
||||
resetComputerUsePermissionsMock.mockReturnValue(result)
|
||||
|
||||
registerComputerUsePermissionHandlers()
|
||||
|
||||
const registration = handleMock.mock.calls.find(
|
||||
([channel]) => channel === 'computerUsePermissions:reset'
|
||||
)
|
||||
expect(registration).toBeTruthy()
|
||||
|
||||
await expect(registration![1]()).resolves.toBe(result)
|
||||
expect(resetComputerUsePermissionsMock).toHaveBeenCalledWith()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -123,6 +123,11 @@ const METHODS = [
|
||||
params: z.object({}),
|
||||
handler: () => ({ permissions: [] })
|
||||
}),
|
||||
defineMethod({
|
||||
name: 'computer.permissionsReset',
|
||||
params: z.object({}),
|
||||
handler: () => ({ permissions: [] })
|
||||
}),
|
||||
defineMethod({
|
||||
name: 'computer.click',
|
||||
params: z.object({}),
|
||||
@@ -162,13 +167,14 @@ describe('RpcDispatcher feature interactions', () => {
|
||||
|
||||
await dispatcher.dispatch(makeRequest('computer.permissions'))
|
||||
await dispatcher.dispatch(makeRequest('computer.permissionsStatus'))
|
||||
await dispatcher.dispatch(makeRequest('computer.permissionsReset'))
|
||||
await dispatcher.dispatch(makeRequest('browser.profileImportFromBrowser'))
|
||||
await dispatcher.dispatch(makeRequest('browser.profileList'))
|
||||
await dispatcher.dispatch(makeRequest('browser.profileClearDefaultCookies'))
|
||||
|
||||
expect(runtime.recordFeatureInteraction).toHaveBeenCalledWith('computer-use-setup')
|
||||
expect(runtime.recordFeatureInteraction).toHaveBeenCalledWith('cookie-import')
|
||||
expect(runtime.recordFeatureInteraction).toHaveBeenCalledTimes(2)
|
||||
expect(runtime.recordFeatureInteraction).toHaveBeenCalledTimes(3)
|
||||
})
|
||||
|
||||
it('does not record failed runtime methods', async () => {
|
||||
|
||||
@@ -10,7 +10,8 @@ const computerMocks = vi.hoisted(() => ({
|
||||
callComputerSidecarSnapshot: vi.fn(),
|
||||
resetComputerSidecarForTest: vi.fn(),
|
||||
openComputerUsePermissions: vi.fn(),
|
||||
getComputerUsePermissionStatus: vi.fn()
|
||||
getComputerUsePermissionStatus: vi.fn(),
|
||||
resetComputerUsePermissions: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('../../../computer/sidecar-client', () => ({
|
||||
@@ -24,7 +25,8 @@ vi.mock('../../../computer/sidecar-client', () => ({
|
||||
|
||||
vi.mock('../../../computer/macos-computer-use-permissions', () => ({
|
||||
openComputerUsePermissions: computerMocks.openComputerUsePermissions,
|
||||
getComputerUsePermissionStatus: computerMocks.getComputerUsePermissionStatus
|
||||
getComputerUsePermissionStatus: computerMocks.getComputerUsePermissionStatus,
|
||||
resetComputerUsePermissions: computerMocks.resetComputerUsePermissions
|
||||
}))
|
||||
|
||||
import { COMPUTER_METHODS, resetComputerSessionsForTest } from './computer'
|
||||
@@ -39,6 +41,7 @@ describe('computer RPC methods', () => {
|
||||
computerMocks.resetComputerSidecarForTest.mockReset()
|
||||
computerMocks.openComputerUsePermissions.mockReset()
|
||||
computerMocks.getComputerUsePermissionStatus.mockReset()
|
||||
computerMocks.resetComputerUsePermissions.mockReset()
|
||||
resetComputerSessionsForTest()
|
||||
computerMocks.resetComputerSidecarForTest.mockClear()
|
||||
})
|
||||
@@ -57,6 +60,7 @@ describe('computer RPC methods', () => {
|
||||
'computer.pasteText',
|
||||
'computer.performSecondaryAction',
|
||||
'computer.permissions',
|
||||
'computer.permissionsReset',
|
||||
'computer.permissionsStatus',
|
||||
'computer.pressKey',
|
||||
'computer.scroll',
|
||||
@@ -121,6 +125,23 @@ describe('computer RPC methods', () => {
|
||||
expect(computerMocks.getComputerUsePermissionStatus).toHaveBeenCalledWith()
|
||||
})
|
||||
|
||||
it('resets all computer-use permissions', async () => {
|
||||
const result = {
|
||||
platform: 'darwin',
|
||||
helperAppPath: '/Applications/Orca Computer Use.app',
|
||||
helperUnavailableReason: null,
|
||||
bundleId: 'com.stablyai.orca.computer-use',
|
||||
permissions: [
|
||||
{ id: 'accessibility', status: 'not-granted' },
|
||||
{ id: 'screenshots', status: 'not-granted' }
|
||||
]
|
||||
}
|
||||
computerMocks.resetComputerUsePermissions.mockReturnValue(result)
|
||||
|
||||
await expect(call('computer.permissionsReset', {})).resolves.toBe(result)
|
||||
expect(computerMocks.resetComputerUsePermissions).toHaveBeenCalledWith()
|
||||
})
|
||||
|
||||
it('lists windows through the sidecar', async () => {
|
||||
const result = {
|
||||
app: { name: 'Finder', bundleId: 'com.apple.finder', pid: 100 },
|
||||
|
||||
@@ -61,6 +61,15 @@ export const COMPUTER_METHODS: RpcMethod[] = [
|
||||
return getComputerUsePermissionStatus()
|
||||
}
|
||||
}),
|
||||
defineMethod({
|
||||
name: 'computer.permissionsReset',
|
||||
params: z.object({}),
|
||||
handler: async () => {
|
||||
const { resetComputerUsePermissions } =
|
||||
await import('../../../computer/macos-computer-use-permissions')
|
||||
return resetComputerUsePermissions()
|
||||
}
|
||||
}),
|
||||
defineMethod({
|
||||
name: 'computer.listWindows',
|
||||
params: ListWindows,
|
||||
|
||||
@@ -25,7 +25,8 @@ export function getRuntimeFeatureInteractionId(
|
||||
if (method.startsWith('emulator.')) {
|
||||
return null
|
||||
}
|
||||
if (method === 'computer.permissions') {
|
||||
// Opening setup and resetting grants are both permission-management flows.
|
||||
if (method === 'computer.permissions' || method === 'computer.permissionsReset') {
|
||||
return 'computer-use-setup'
|
||||
}
|
||||
if (
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
RefreshAgentsResult
|
||||
} from '../../../../preload/api-types'
|
||||
import type {
|
||||
ComputerUsePermissionResetResult,
|
||||
ComputerUsePermissionSetupResult,
|
||||
ComputerUsePermissionStatusResult
|
||||
} from '../../../../shared/computer-use-permissions-types'
|
||||
@@ -132,13 +133,20 @@ export function createComputerUsePermissionsApi(): NonNullable<
|
||||
nextStep: 'Computer-use permissions are managed on the Orca server.'
|
||||
})),
|
||||
reset: () =>
|
||||
Promise.resolve({
|
||||
platform: getBrowserPlatform(),
|
||||
helperAppPath: null,
|
||||
helperUnavailableReason: 'web_client',
|
||||
bundleId: null,
|
||||
permissions: []
|
||||
})
|
||||
callRuntimeResult<ComputerUsePermissionResetResult>(
|
||||
'computer.permissionsReset',
|
||||
{},
|
||||
15_000
|
||||
).catch(
|
||||
() =>
|
||||
({
|
||||
platform: getBrowserPlatform(),
|
||||
helperAppPath: null,
|
||||
helperUnavailableReason: 'web_client',
|
||||
bundleId: null,
|
||||
permissions: []
|
||||
}) satisfies ComputerUsePermissionResetResult
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { RuntimeRpcResponse } from '../../../shared/runtime-rpc-envelope'
|
||||
import {
|
||||
installBrowserGlobals,
|
||||
writeStoredRuntimeEnvironment
|
||||
} from './web-preload-api-test-harness'
|
||||
|
||||
describe('web UI computer-use permission reset', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals()
|
||||
vi.doUnmock('./web-runtime-client')
|
||||
})
|
||||
|
||||
it('proxies permission reset for paired web clients', async () => {
|
||||
const calls: { method: string; params: unknown }[] = []
|
||||
vi.doMock('./web-runtime-client', () => ({
|
||||
WebRuntimeClient: class {
|
||||
call(method: string, params: unknown): Promise<RuntimeRpcResponse<unknown>> {
|
||||
calls.push({ method, params })
|
||||
return Promise.resolve({
|
||||
id: method,
|
||||
ok: true,
|
||||
result:
|
||||
method === 'computer.permissionsReset'
|
||||
? {
|
||||
platform: 'darwin',
|
||||
helperAppPath: '/Applications/Orca Computer Use.app',
|
||||
helperUnavailableReason: null,
|
||||
bundleId: 'com.stablyai.orca.computer-use',
|
||||
permissions: [
|
||||
{ id: 'accessibility', status: 'not-granted' },
|
||||
{ id: 'screenshots', status: 'not-granted' }
|
||||
]
|
||||
}
|
||||
: {},
|
||||
_meta: { runtimeId: 'runtime-1' }
|
||||
})
|
||||
}
|
||||
|
||||
close(): void {}
|
||||
}
|
||||
}))
|
||||
|
||||
const globals = installBrowserGlobals('Linux')
|
||||
writeStoredRuntimeEnvironment(globals.storage)
|
||||
const { installWebPreloadApi } = await import('./web-preload-api')
|
||||
installWebPreloadApi()
|
||||
|
||||
await expect(globals.window.api.computerUsePermissions.reset()).resolves.toMatchObject({
|
||||
bundleId: 'com.stablyai.orca.computer-use',
|
||||
permissions: [
|
||||
{ id: 'accessibility', status: 'not-granted' },
|
||||
{ id: 'screenshots', status: 'not-granted' }
|
||||
]
|
||||
})
|
||||
expect(calls).toEqual([{ method: 'computer.permissionsReset', params: {} }])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user