import { getSentinelCategory } from '@ant/computer-use-mcp/sentinelApps' import type { CuPermissionRequest, CuPermissionResponse, } from '@ant/computer-use-mcp/types' import { DEFAULT_GRANT_FLAGS } from '@ant/computer-use-mcp/types' import figures from 'figures' import * as React from 'react' import { useMemo, useState } from 'react' import { Box, Text } from '../../../ink.js' import { execFileNoThrow } from '../../../utils/execFileNoThrow.js' import { plural } from '../../../utils/stringUtils.js' import type { OptionWithDescription } from '../../CustomSelect/select.js' import { Select } from '../../CustomSelect/select.js' import { Dialog } from '../../design-system/Dialog.js' type ComputerUseApprovalProps = { request: CuPermissionRequest onDone: (response: CuPermissionResponse) => void } const DENY_ALL_RESPONSE: CuPermissionResponse = { granted: [], denied: [], flags: DEFAULT_GRANT_FLAGS, } /** * Two-panel dispatcher. When `request.tccState` is present, macOS permissions * (Accessibility / Screen Recording) are missing and the app list is * irrelevant — show a TCC panel that opens System Settings. Otherwise show the * app allowlist + grant-flags panel. */ export function ComputerUseApproval({ request, onDone, }: ComputerUseApprovalProps): React.ReactNode { return request.tccState ? ( onDone(DENY_ALL_RESPONSE)} /> ) : ( ) } // ── TCC panel ───────────────────────────────────────────────────────────── type TccOption = 'open_accessibility' | 'open_screen_recording' | 'retry' function ComputerUseTccPanel({ tccState, onDone, }: { tccState: NonNullable onDone: () => void }): React.ReactNode { const options = useMemo[]>(() => { const opts: OptionWithDescription[] = [] if (!tccState.accessibility) { opts.push({ label: 'Open System Settings → Accessibility', value: 'open_accessibility', }) } if (!tccState.screenRecording) { opts.push({ label: 'Open System Settings → Screen Recording', value: 'open_screen_recording', }) } opts.push({ label: 'Try again', value: 'retry' }) return opts }, [tccState.accessibility, tccState.screenRecording]) function onChange(value: TccOption): void { switch (value) { case 'open_accessibility': void execFileNoThrow( 'open', [ 'x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility', ], { useCwd: false }, ) return case 'open_screen_recording': void execFileNoThrow( 'open', [ 'x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture', ], { useCwd: false }, ) return case 'retry': // Resolve with deny-all — the model re-calls request_access, which // re-checks TCC and renders the app list if now granted. onDone() return } } return ( Accessibility:{' '} {tccState.accessibility ? `${figures.tick} granted` : `${figures.cross} not granted`} Screen Recording:{' '} {tccState.screenRecording ? `${figures.tick} granted` : `${figures.cross} not granted`} Grant the missing permissions in System Settings, then select "Try again". macOS may require you to restart Claude Code after granting Screen Recording. respond(v === 'allow_all')} onCancel={() => respond(false)} /> ) }