diff --git a/README.md b/README.md index a4e7cf0538..a1d84e9a6b 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,19 @@ verboo /login - **Tool calling**: Multi-step tool loops with model calls, tool execution, and follow-up responses - **Images**: URL and base64 image inputs for providers that support vision +## Verboo in Chrome + +The Verboo desktop app installs and maintains the local `verboo-in-chrome` MCP +on macOS, Windows, and Linux. Configure it in **Settings > Integrations > +Chrome**. Once the managed MCP entry is present, the CLI connects to it +automatically and exposes the `verboo-in-chrome` skill for browser tasks. + +Use `verboo --no-chrome` to disable the integration for one session, +`verboo --chrome` to require it for one session, and `/chrome` to inspect its +status. The transport stays local, does not copy CLI credentials into the +extension, and continues to enforce the extension's site permissions and +approval flow. + ## Web Search and Fetch `WebSearch` uses DuckDuckGo by default. diff --git a/src/commands/chrome/chrome.tsx b/src/commands/chrome/chrome.tsx index 6b933ba24c..d30dbe7aa4 100644 --- a/src/commands/chrome/chrome.tsx +++ b/src/commands/chrome/chrome.tsx @@ -1,284 +1,75 @@ -import { c as _c } from "react-compiler-runtime"; -import React, { useState } from 'react'; -import { type OptionWithDescription, Select } from '../../components/CustomSelect/select.js'; -import { Dialog } from '../../components/design-system/Dialog.js'; -import { Box, Text } from '../../ink.js'; -import { useAppState } from '../../state/AppState.js'; -import { isClaudeAISubscriber } from '../../utils/auth.js'; -import { openBrowser } from '../../utils/browser.js'; -import { CLAUDE_IN_CHROME_MCP_SERVER_NAME, openInChrome } from '../../utils/claudeInChrome/common.js'; -import { isChromeExtensionInstalled } from '../../utils/claudeInChrome/setup.js'; -import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'; -import { env } from '../../utils/env.js'; -import { isRunningOnHomespace } from '../../utils/envUtils.js'; -const CHROME_EXTENSION_URL = 'https://claude.ai/chrome'; -const CHROME_PERMISSIONS_URL = 'https://clau.de/chrome/permissions'; -const CHROME_RECONNECT_URL = 'https://clau.de/chrome/reconnect'; -type MenuAction = 'install-extension' | 'reconnect' | 'manage-permissions' | 'toggle-default'; +import type React from 'react' +import { Box, Text } from '../../ink.js' +import { useAppState } from '../../state/AppState.js' +import { getGlobalConfig } from '../../utils/config.js' +import { + VERBOO_IN_CHROME_MCP_SERVER_NAME, + isManagedVerbooInChromeConfig, + isVerbooInChromeMcpServer, +} from '../../utils/verbooInChrome.js' +import { Dialog } from '../../components/design-system/Dialog.js' + type Props = { - onDone: (result?: string) => void; - isExtensionInstalled: boolean; - configEnabled: boolean | undefined; - isClaudeAISubscriber: boolean; - isWSL: boolean; -}; -function ClaudeInChromeMenu(t0) { - const $ = _c(41); - const { - onDone, - isExtensionInstalled: installed, - configEnabled, - isClaudeAISubscriber, - isWSL - } = t0; - const mcpClients = useAppState(_temp); - const [selectKey, setSelectKey] = useState(0); - const [enabledByDefault, setEnabledByDefault] = useState(configEnabled ?? false); - const [showInstallHint, setShowInstallHint] = useState(false); - const [isExtensionInstalled, setIsExtensionInstalled] = useState(installed); - let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t1 = false && isRunningOnHomespace(); - $[0] = t1; - } else { - t1 = $[0]; - } - const isHomespace = t1; - let t2; - if ($[1] !== mcpClients) { - t2 = mcpClients.find(_temp2); - $[1] = mcpClients; - $[2] = t2; - } else { - t2 = $[2]; - } - const chromeClient = t2; - const isConnected = chromeClient?.type === "connected"; - let t3; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t3 = function openUrl(url) { - if (isHomespace) { - openBrowser(url); - } else { - openInChrome(url); - } - }; - $[3] = t3; - } else { - t3 = $[3]; - } - const openUrl = t3; - let t4; - if ($[4] !== enabledByDefault) { - t4 = function handleAction(action) { - bb22: switch (action) { - case "install-extension": - { - setSelectKey(_temp3); - setShowInstallHint(true); - openUrl(CHROME_EXTENSION_URL); - break bb22; - } - case "reconnect": - { - setSelectKey(_temp4); - isChromeExtensionInstalled().then(installed_0 => { - setIsExtensionInstalled(installed_0); - if (installed_0) { - setShowInstallHint(false); - } - }); - openUrl(CHROME_RECONNECT_URL); - break bb22; - } - case "manage-permissions": - { - setSelectKey(_temp5); - openUrl(CHROME_PERMISSIONS_URL); - break bb22; - } - case "toggle-default": - { - const newValue = !enabledByDefault; - saveGlobalConfig(current => ({ - ...current, - claudeInChromeDefaultEnabled: newValue - })); - setEnabledByDefault(newValue); - } - } - }; - $[4] = enabledByDefault; - $[5] = t4; - } else { - t4 = $[5]; - } - const handleAction = t4; - let options; - if ($[6] !== enabledByDefault || $[7] !== isExtensionInstalled) { - options = []; - const requiresExtensionSuffix = isExtensionInstalled ? "" : " (requires extension)"; - if (!isExtensionInstalled && !isHomespace) { - let t5; - if ($[9] === Symbol.for("react.memo_cache_sentinel")) { - t5 = { - label: "Install Chrome extension", - value: "install-extension" - }; - $[9] = t5; - } else { - t5 = $[9]; - } - options.push(t5); - } - let t5; - if ($[10] === Symbol.for("react.memo_cache_sentinel")) { - t5 = Manage permissions; - $[10] = t5; - } else { - t5 = $[10]; - } - let t6; - if ($[11] !== requiresExtensionSuffix) { - t6 = { - label: <>{t5}{requiresExtensionSuffix}, - value: "manage-permissions" - }; - $[11] = requiresExtensionSuffix; - $[12] = t6; - } else { - t6 = $[12]; - } - let t7; - if ($[13] === Symbol.for("react.memo_cache_sentinel")) { - t7 = Reconnect extension; - $[13] = t7; - } else { - t7 = $[13]; - } - let t8; - if ($[14] !== requiresExtensionSuffix) { - t8 = { - label: <>{t7}{requiresExtensionSuffix}, - value: "reconnect" - }; - $[14] = requiresExtensionSuffix; - $[15] = t8; - } else { - t8 = $[15]; - } - const t9 = `Enabled by default: ${enabledByDefault ? "Yes" : "No"}`; - let t10; - if ($[16] !== t9) { - t10 = { - label: t9, - value: "toggle-default" - }; - $[16] = t9; - $[17] = t10; - } else { - t10 = $[17]; - } - options.push(t6, t8, t10); - $[6] = enabledByDefault; - $[7] = isExtensionInstalled; - $[8] = options; - } else { - options = $[8]; - } - const isDisabled = isWSL || true && !isClaudeAISubscriber; - let t5; - if ($[18] !== onDone) { - t5 = () => onDone(); - $[18] = onDone; - $[19] = t5; - } else { - t5 = $[19]; - } - let t6; - if ($[20] === Symbol.for("react.memo_cache_sentinel")) { - t6 = Claude in Chrome works with the Chrome extension to let you control your browser directly from Verboo Code. Navigate websites, fill forms, capture screenshots, record GIFs, and debug with console logs and network requests.; - $[20] = t6; - } else { - t6 = $[20]; - } - let t7; - if ($[21] !== isWSL) { - t7 = isWSL && Claude in Chrome is not supported in WSL at this time.; - $[21] = isWSL; - $[22] = t7; - } else { - t7 = $[22]; - } - let t8; - if ($[23] !== isClaudeAISubscriber) { - t8 = true && !isClaudeAISubscriber && Claude in Chrome requires a claude.ai subscription.; - $[23] = isClaudeAISubscriber; - $[24] = t8; - } else { - t8 = $[24]; - } - let t9; - if ($[25] !== handleAction || $[26] !== isConnected || $[27] !== isDisabled || $[28] !== isExtensionInstalled || $[29] !== options || $[30] !== selectKey || $[31] !== showInstallHint) { - t9 = !isDisabled && <>{!isHomespace && Status:{" "}{isConnected ? Enabled : Disabled}Extension:{" "}{isExtensionInstalled ? Installed : Not detected}}