import { useEffect, useState } from "react"; import { Button } from "@ace/ui"; import type { NativeAction, NativeState } from "@ace/desktop/protocol"; type Permission = Extract["permission"]; const permissions = [ { key: "accessibility", label: "Accessibility" }, { key: "screenRecording", label: "Screen Recording" }, { key: "eventSynthesizing", label: "Keyboard input" }, ] as const; const states: Record = { stopped: "Not running", starting: "Starting…", ready: "Ready", stopping: "Stopping…", error: "Needs attention", }; export function NativeStatus( { native, refresh }: { native: (action: NativeAction) => Promise; refresh: number }, ) { const [result, setResult] = useState<{ value?: NativeState; error?: string }>({}); const [action, setAction] = useState<{ busy?: Permission; error?: string }>({}); const [retry, setRetry] = useState(0); useEffect(() => { let active = true; let pending = false; const check = () => { if (document.visibilityState !== "visible" || pending) return; pending = true; native({ op: "status" }).then( (value) => { if (active) setResult({ value }); }, (error: Error) => { if (active) setResult({ error: error.message }); }, ).finally(() => { pending = false; }); }; check(); const timer = window.setInterval(check, 3000); window.addEventListener("focus", check); document.addEventListener("visibilitychange", check); return () => { active = false; window.clearInterval(timer); window.removeEventListener("focus", check); document.removeEventListener("visibilitychange", check); }; }, [native, refresh, retry]); async function grant(permission: Permission) { setAction({ busy: permission }); try { const value = await native({ op: "permission", permission }); setResult({ value }); setAction(value.error ? { error: value.error } : {}); } catch (error) { setAction({ error: (error as Error).message }); } setRetry(value => value + 1); } const value = result.value; const needsPermission = value?.state === "ready" && permissions.some(({ key }) => !value[key]); const error = action.error || result.error || value?.error; return (

Computer use

{needsPermission ? "Permissions needed" : value ? states[value.state] : result.error ? "Could not check computer use" : "Checking computer use…"}

Keep Ace open on the host running the channel's tools. For a hosted channel, that is its workspace. The channel's collaborator agent setting also applies to computer use.

{permissions.map(({ key, label }) => (

{label}: {value ? value[key] ? "Granted" : "Not granted" : "Not checked"}

{!value?.[key] && ( )}
))}
{action.busy &&

Requesting permission…

} {error &&

{error}

}
); }