From a35200f340243139a79c00895a846144468f9617 Mon Sep 17 00:00:00 2001 From: Simos Mikelatos Date: Fri, 19 Jun 2026 08:06:16 +0000 Subject: [PATCH] Harden computer use MCP handling --- electron/launcher/launcher.css | 11 +++++------ .../computer-use/computer-use-mcp.routes.ts | 14 ++++++++++++-- .../computer-use/computer-use.service.ts | 19 ++++++++++++++++--- .../ComputerUseSettingsTab.tsx | 16 ++-------------- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/electron/launcher/launcher.css b/electron/launcher/launcher.css index b361cd18..100913bd 100644 --- a/electron/launcher/launcher.css +++ b/electron/launcher/launcher.css @@ -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 { diff --git a/server/modules/computer-use/computer-use-mcp.routes.ts b/server/modules/computer-use/computer-use-mcp.routes.ts index 0840a415..8045f4f2 100644 --- a/server/modules/computer-use/computer-use-mcp.routes.ts +++ b/server/modules/computer-use/computer-use-mcp.routes.ts @@ -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' { diff --git a/server/modules/computer-use/computer-use.service.ts b/server/modules/computer-use/computer-use.service.ts index 60892be9..cef22450 100644 --- a/server/modules/computer-use/computer-use.service.ts +++ b/server/modules/computer-use/computer-use.service.ts @@ -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 { 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) { 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; diff --git a/src/components/settings/view/tabs/computer-use-settings/ComputerUseSettingsTab.tsx b/src/components/settings/view/tabs/computer-use-settings/ComputerUseSettingsTab.tsx index b1fbac10..0bf31e73 100644 --- a/src/components/settings/view/tabs/computer-use-settings/ComputerUseSettingsTab.tsx +++ b/src/components/settings/view/tabs/computer-use-settings/ComputerUseSettingsTab.tsx @@ -111,28 +111,16 @@ export default function ComputerUseSettingsTab() { void updateSettings({ enabled: value })} + onChange={(value) => void updateSettings({ enabled: value, agentToolsEnabled: value })} ariaLabel="Enable Computer Use" disabled={isLoading || isSaving} /> - - void updateSettings({ agentToolsEnabled: value })} - ariaLabel="Enable Computer Tools for Agents" - disabled={isLoading || isSaving || !settings.enabled} - /> - - {(needsRuntime || isCloud || error) && (
{isCloud && (