Harden computer use MCP handling

This commit is contained in:
Simos Mikelatos
2026-06-19 08:06:16 +00:00
parent 06c9745489
commit a35200f340
4 changed files with 35 additions and 25 deletions

View File

@@ -279,8 +279,7 @@ svg {
.cc-overlay {
position: fixed;
inset: 0;
background: rgba(6, 8, 11, 0.46);
backdrop-filter: blur(16px);
background: rgba(6, 8, 11, 0.28);
display: none;
z-index: 50;
align-items: center;
@@ -295,14 +294,14 @@ svg {
.cc-sheet {
width: 620px;
max-width: min(92vw, 620px);
max-height: 86vh;
max-height: min(720px, 82vh);
overflow: hidden;
display: flex;
flex-direction: column;
border-radius: 18px;
border-radius: 14px;
border: 1px solid var(--b);
background: color-mix(in srgb, var(--s1) 94%, transparent);
box-shadow: 0 32px 80px rgba(0, 0, 0, 0.32);
background: color-mix(in srgb, var(--s1) 98%, transparent);
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.34);
}
.cc-sheet-header {

View File

@@ -8,8 +8,18 @@ function readBearerToken(header: unknown): string | null {
if (typeof header !== 'string') {
return null;
}
const match = /^Bearer\s+(.+)$/i.exec(header.trim());
return match?.[1] || null;
const trimmed = header.trim();
const scheme = 'Bearer';
if (trimmed.slice(0, scheme.length).toLowerCase() !== scheme.toLowerCase()) {
return null;
}
const separator = trimmed[scheme.length];
if (separator !== ' ' && separator !== '\t') {
return null;
}
return trimmed.slice(scheme.length + 1).trimStart() || null;
}
function toButton(value: unknown): 'left' | 'right' | 'middle' {

View File

@@ -22,6 +22,8 @@ const MAX_SESSIONS_PER_OWNER = Number.parseInt(process.env.CLOUDCLI_COMPUTER_USE
const SESSION_TTL_MS = Number.parseInt(process.env.CLOUDCLI_COMPUTER_USE_SESSION_TTL_MS || String(30 * 60 * 1000), 10);
const COMPUTER_USE_SETTINGS_KEY = 'computer_use_settings';
const COMPUTER_USE_MCP_TOKEN_KEY = 'computer_use_mcp_token';
const DEFAULT_AGENT_WAIT_MS = 1000;
const MAX_AGENT_WAIT_MS = 10_000;
type ComputerUseRuntime = 'cloud' | 'local';
type ComputerUseSessionStatus = 'ready' | 'stopped' | 'unavailable';
@@ -331,6 +333,16 @@ function applyRelayResult(session: ComputerUseSession, result: RelayResult): voi
session.updatedAt = new Date().toISOString();
}
function normalizeAgentWaitMs(ms: number | undefined): number {
if (ms === undefined) {
return DEFAULT_AGENT_WAIT_MS;
}
if (!Number.isFinite(ms)) {
throw new Error('Computer Use wait duration must be a finite number.');
}
return Math.trunc(Math.max(0, Math.min(ms, MAX_AGENT_WAIT_MS)));
}
async function refreshScreenshot(session: ComputerUseSession): Promise<void> {
if (getRuntime() === 'cloud') {
const result = (await desktopAgentRelay.relay('screenshot', { sessionId: session.id })) as RelayResult;
@@ -379,7 +391,7 @@ async function performAction(session: ComputerUseSession, action: ComputerAction
await executor.scroll(session, action.direction, action.amount ?? 3, action.point);
break;
case 'wait':
await new Promise((resolve) => setTimeout(resolve, Math.max(0, Math.min(action.ms ?? 1000, 10_000))));
await new Promise((resolve) => setTimeout(resolve, normalizeAgentWaitMs(action.ms)));
break;
}
await refreshScreenshot(session);
@@ -437,12 +449,13 @@ export const computerUseService = {
async updateSettings(settings: Partial<ComputerUseSettings>) {
const current = readSettings();
const enabled = typeof settings.enabled === 'boolean' ? settings.enabled : current.enabled;
const nextSettings = {
...current,
enabled: typeof settings.enabled === 'boolean' ? settings.enabled : current.enabled,
enabled,
agentToolsEnabled: typeof settings.agentToolsEnabled === 'boolean'
? settings.agentToolsEnabled
: current.agentToolsEnabled,
: enabled,
};
if (!nextSettings.enabled) {
nextSettings.agentToolsEnabled = false;

View File

@@ -111,28 +111,16 @@ export default function ComputerUseSettingsTab() {
<SettingsRow
label="Enable Computer Use"
description="Allow CloudCLI to capture the screen and create desktop control sessions on this machine."
description="Registers Computer Use for supported agents and allows CloudCLI to create guarded desktop control sessions on this machine."
>
<SettingsToggle
checked={settings.enabled}
onChange={(value) => void updateSettings({ enabled: value })}
onChange={(value) => void updateSettings({ enabled: value, agentToolsEnabled: value })}
ariaLabel="Enable Computer Use"
disabled={isLoading || isSaving}
/>
</SettingsRow>
<SettingsRow
label="Enable Computer Tools for Agents"
description="Register the Computer Use MCP server for all agent providers. Agents can request desktop control, but actions require your explicit per-session consent."
>
<SettingsToggle
checked={settings.agentToolsEnabled}
onChange={(value) => void updateSettings({ agentToolsEnabled: value })}
ariaLabel="Enable Computer Tools for Agents"
disabled={isLoading || isSaving || !settings.enabled}
/>
</SettingsRow>
{(needsRuntime || isCloud || error) && (
<div className="space-y-4 px-4 py-4">
{isCloud && (