From 6e7e2ff4c1b21c05253ed0dd6edf7228a962243c Mon Sep 17 00:00:00 2001 From: Simos Mikelatos Date: Mon, 15 Jun 2026 18:12:27 +0000 Subject: [PATCH] feat: make browser use opt-in --- .../browser-use/browser-use.service.ts | 23 ++++++++++---- src/components/main-content/types/types.ts | 1 + .../main-content/view/MainContent.tsx | 30 +++++++++++++++++-- .../view/subcomponents/MainContentHeader.tsx | 2 ++ .../subcomponents/MainContentTabSwitcher.tsx | 16 ++++++++-- .../BrowserUseSettingsTab.tsx | 3 +- 6 files changed, 65 insertions(+), 10 deletions(-) diff --git a/server/modules/browser-use/browser-use.service.ts b/server/modules/browser-use/browser-use.service.ts index df99925c..bb53147f 100644 --- a/server/modules/browser-use/browser-use.service.ts +++ b/server/modules/browser-use/browser-use.service.ts @@ -61,7 +61,7 @@ let installPromise: Promise<{ success: boolean; message: string }> | null = null let lastInstallMessage: string | null = null; const DEFAULT_SETTINGS: BrowserUseSettings = { - enabled: true, + enabled: false, }; function getRuntime(): BrowserUseRuntime { @@ -77,7 +77,7 @@ function readSettings(): BrowserUseSettings { const parsed = JSON.parse(raw) as Partial; return { - enabled: parsed.enabled !== false, + enabled: parsed.enabled === true, }; } catch (error: any) { console.warn('[Browser Use] Failed to read settings:', error?.message || error); @@ -87,7 +87,7 @@ function readSettings(): BrowserUseSettings { function writeSettings(settings: BrowserUseSettings): BrowserUseSettings { const normalized = { - enabled: settings.enabled !== false, + enabled: settings.enabled === true, }; appConfigDb.set(BROWSER_USE_SETTINGS_KEY, JSON.stringify(normalized)); @@ -168,6 +168,14 @@ function runCommand(command: string, args: string[]): Promise { }); } +function formatInstallError(error: unknown): string { + const message = error instanceof Error ? error.message : String(error); + if (message.includes('sudo') && message.includes('password')) { + return 'Installing Chromium system dependencies requires administrator privileges. Run `npx playwright install-deps chromium` on the machine where CloudCLI runs, then try again.'; + } + return message || 'Failed to install Browser Use runtime.'; +} + async function installRuntime(): Promise<{ success: boolean; message: string }> { if (installPromise) { return installPromise; @@ -179,13 +187,18 @@ async function installRuntime(): Promise<{ success: boolean; message: string }> lastInstallMessage = 'Installing Playwright package...'; await runCommand(npmCommand, ['install', '--no-save', '--no-package-lock', 'playwright']); + if (process.platform === 'linux') { + lastInstallMessage = 'Installing Chromium system dependencies...'; + await runCommand(npmCommand, ['exec', '--', 'playwright', 'install-deps', 'chromium']); + } + lastInstallMessage = 'Installing Chromium runtime...'; await runCommand(npmCommand, ['exec', '--', 'playwright', 'install', 'chromium']); lastInstallMessage = 'Browser Use runtime installed.'; return { success: true, message: lastInstallMessage }; } catch (error) { - lastInstallMessage = error instanceof Error ? error.message : 'Failed to install Browser Use runtime.'; + lastInstallMessage = formatInstallError(error); return { success: false, message: lastInstallMessage }; } })(); @@ -359,7 +372,7 @@ export const browserUseService = { const current = readSettings(); return writeSettings({ ...current, - enabled: settings.enabled ?? current.enabled, + enabled: typeof settings.enabled === 'boolean' ? settings.enabled : current.enabled, }); }, diff --git a/src/components/main-content/types/types.ts b/src/components/main-content/types/types.ts index a7398795..6ae16ec8 100644 --- a/src/components/main-content/types/types.ts +++ b/src/components/main-content/types/types.ts @@ -64,6 +64,7 @@ export type MainContentHeaderProps = { selectedProject: Project; selectedSession: ProjectSession | null; shouldShowTasksTab: boolean; + shouldShowBrowserTab: boolean; isMobile: boolean; onMenuClick: () => void; }; diff --git a/src/components/main-content/view/MainContent.tsx b/src/components/main-content/view/MainContent.tsx index 12cfe7aa..5facdb43 100644 --- a/src/components/main-content/view/MainContent.tsx +++ b/src/components/main-content/view/MainContent.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import ChatInterface from '../../chat/view/ChatInterface'; import FileTree from '../../file-tree/view/FileTree'; @@ -11,6 +11,7 @@ import { useTaskMaster } from '../../../contexts/TaskMasterContext'; import { usePaletteOpsRegister } from '../../../contexts/PaletteOpsContext'; import { useTasksSettings } from '../../../contexts/TasksSettingsContext'; import { useUiPreferences } from '../../../hooks/useUiPreferences'; +import { authenticatedFetch } from '../../../utils/api'; import { useEditorSidebar } from '../../code-editor/hooks/useEditorSidebar'; import EditorSidebar from '../../code-editor/view/EditorSidebar'; import type { Project } from '../../../types/app'; @@ -56,8 +57,10 @@ function MainContent({ const { currentProject, setCurrentProject } = useTaskMaster() as TaskMasterContextValue; const { tasksEnabled, isTaskMasterInstalled } = useTasksSettings() as TasksSettingsContextValue; + const [browserUseEnabled, setBrowserUseEnabled] = useState(false); const shouldShowTasksTab = Boolean(tasksEnabled && isTaskMasterInstalled); + const shouldShowBrowserTab = browserUseEnabled; const { editingFile, @@ -91,6 +94,28 @@ function MainContent({ } }, [shouldShowTasksTab, activeTab, setActiveTab]); + const loadBrowserUseSettings = useCallback(async () => { + try { + const response = await authenticatedFetch('/api/browser-use/settings'); + const data = await response.json(); + setBrowserUseEnabled(Boolean(response.ok && data?.success !== false && data?.data?.settings?.enabled)); + } catch { + setBrowserUseEnabled(false); + } + }, []); + + useEffect(() => { + void loadBrowserUseSettings(); + window.addEventListener('browserUseSettingsChanged', loadBrowserUseSettings); + return () => window.removeEventListener('browserUseSettingsChanged', loadBrowserUseSettings); + }, [loadBrowserUseSettings]); + + useEffect(() => { + if (!shouldShowBrowserTab && activeTab === 'browser') { + setActiveTab('chat'); + } + }, [shouldShowBrowserTab, activeTab, setActiveTab]); + usePaletteOpsRegister({ openFile: (filePath: string) => { setActiveTab('files'); @@ -114,6 +139,7 @@ function MainContent({ selectedProject={selectedProject} selectedSession={selectedSession} shouldShowTasksTab={shouldShowTasksTab} + shouldShowBrowserTab={shouldShowBrowserTab} isMobile={isMobile} onMenuClick={onMenuClick} /> @@ -172,7 +198,7 @@ function MainContent({ {shouldShowTasksTab && } - {activeTab === 'browser' && ( + {shouldShowBrowserTab && activeTab === 'browser' && (
diff --git a/src/components/main-content/view/subcomponents/MainContentHeader.tsx b/src/components/main-content/view/subcomponents/MainContentHeader.tsx index a9025c2b..f75013ce 100644 --- a/src/components/main-content/view/subcomponents/MainContentHeader.tsx +++ b/src/components/main-content/view/subcomponents/MainContentHeader.tsx @@ -10,6 +10,7 @@ export default function MainContentHeader({ selectedProject, selectedSession, shouldShowTasksTab, + shouldShowBrowserTab, isMobile, onMenuClick, }: MainContentHeaderProps) { @@ -59,6 +60,7 @@ export default function MainContentHeader({ activeTab={activeTab} setActiveTab={setActiveTab} shouldShowTasksTab={shouldShowTasksTab} + shouldShowBrowserTab={shouldShowBrowserTab} /> {canScrollRight && ( diff --git a/src/components/main-content/view/subcomponents/MainContentTabSwitcher.tsx b/src/components/main-content/view/subcomponents/MainContentTabSwitcher.tsx index 83c641df..bffe39d6 100644 --- a/src/components/main-content/view/subcomponents/MainContentTabSwitcher.tsx +++ b/src/components/main-content/view/subcomponents/MainContentTabSwitcher.tsx @@ -11,6 +11,7 @@ type MainContentTabSwitcherProps = { activeTab: AppTab; setActiveTab: Dispatch>; shouldShowTasksTab: boolean; + shouldShowBrowserTab: boolean; }; type BuiltInTab = { @@ -35,9 +36,15 @@ const BASE_TABS: BuiltInTab[] = [ { kind: 'builtin', id: 'shell', labelKey: 'tabs.shell', icon: Terminal }, { kind: 'builtin', id: 'files', labelKey: 'tabs.files', icon: Folder }, { kind: 'builtin', id: 'git', labelKey: 'tabs.git', icon: GitBranch }, - { kind: 'builtin', id: 'browser', labelKey: 'tabs.browser', icon: MonitorPlay }, ]; +const BROWSER_TAB: BuiltInTab = { + kind: 'builtin', + id: 'browser', + labelKey: 'tabs.browser', + icon: MonitorPlay, +}; + const TASKS_TAB: BuiltInTab = { kind: 'builtin', id: 'tasks', @@ -49,11 +56,16 @@ export default function MainContentTabSwitcher({ activeTab, setActiveTab, shouldShowTasksTab, + shouldShowBrowserTab, }: MainContentTabSwitcherProps) { const { t } = useTranslation(); const { plugins } = usePlugins(); - const builtInTabs: BuiltInTab[] = shouldShowTasksTab ? [...BASE_TABS, TASKS_TAB] : BASE_TABS; + const builtInTabs: BuiltInTab[] = [ + ...BASE_TABS, + ...(shouldShowBrowserTab ? [BROWSER_TAB] : []), + ...(shouldShowTasksTab ? [TASKS_TAB] : []), + ]; const pluginTabs: PluginTab[] = plugins .filter((p) => p.enabled) diff --git a/src/components/settings/view/tabs/browser-use-settings/BrowserUseSettingsTab.tsx b/src/components/settings/view/tabs/browser-use-settings/BrowserUseSettingsTab.tsx index 4cc0f86b..361482e6 100644 --- a/src/components/settings/view/tabs/browser-use-settings/BrowserUseSettingsTab.tsx +++ b/src/components/settings/view/tabs/browser-use-settings/BrowserUseSettingsTab.tsx @@ -31,7 +31,7 @@ async function readJson(response: Response): Promise { } export default function BrowserUseSettingsTab() { - const [settings, setSettings] = useState({ enabled: true }); + const [settings, setSettings] = useState({ enabled: false }); const [status, setStatus] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); @@ -67,6 +67,7 @@ export default function BrowserUseSettingsTab() { }); const data = await readJson<{ data: { settings: BrowserUseSettings } }>(response); setSettings(data.data.settings); + window.dispatchEvent(new Event('browserUseSettingsChanged')); await loadState(); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to save Browser Use settings');