Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

103 lines
3.8 KiB
Go

package browser
import (
"context"
"encoding/json"
"log/slog"
"os"
"strings"
preferencesbiz "github.com/tutti-os/tutti/services/tuttid/biz/preferences"
managedruntime "github.com/tutti-os/tutti/services/tuttid/service/managedruntime"
)
// chrome-devtools-mcp launch resolution. The daemon now consumes this command
// directly (it runs the MCP server as its own subprocess) instead of
// advertising it to agent providers. Operator overrides via
// TUTTI_BROWSER_MCP_COMMAND / TUTTI_BROWSER_MCP_ARGS still apply. Packaged
// desktop points TUTTI_BROWSER_MCP_ENTRY_PATH at its vendored entry script; dev
// falls back to the pinned npx command below.
const (
browserMCPCommandOverrideEnv = "TUTTI_BROWSER_MCP_COMMAND"
browserMCPArgsOverrideEnv = "TUTTI_BROWSER_MCP_ARGS"
browserMCPEntryPathEnv = "TUTTI_BROWSER_MCP_ENTRY_PATH"
// browserMCPPinnedVersion pins the chrome-devtools-mcp release for the npx
// fallback so dev runs are reproducible.
browserMCPPinnedVersion = "chrome-devtools-mcp@1.2.0"
)
// defaultBrowserMCPCommand / Args launch chrome-devtools-mcp in external-Chrome
// mode (the server manages its own Chrome). Launch-mode args are appended after
// resolving desktop preferences.
var (
defaultBrowserMCPCommand = "npx"
defaultBrowserMCPArgs = []string{"-y", browserMCPPinnedVersion}
)
// resolveBrowserMCPCommand returns the full command (command + args) used to
// launch the browser MCP server, honoring operator overrides.
func resolveBrowserMCPCommand(ctx context.Context, preferences PreferencesReader, runtimeResolver managedruntime.ProfileResolver) []string {
command, args := resolveBrowserMCPBaseCommand(ctx, runtimeResolver)
if raw := strings.TrimSpace(os.Getenv(browserMCPArgsOverrideEnv)); raw != "" {
var override []string
if err := json.Unmarshal([]byte(raw), &override); err == nil {
args = override
return append([]string{command}, args...)
}
}
mode := resolveBrowserUseConnectionMode(ctx, preferences)
args = append(args, resolveBrowserMCPConnectionArgs(mode)...)
return append([]string{command}, args...)
}
func resolveBrowserMCPBaseCommand(ctx context.Context, runtimeResolver managedruntime.ProfileResolver) (string, []string) {
if command := strings.TrimSpace(os.Getenv(browserMCPCommandOverrideEnv)); command != "" {
return command, []string{}
}
if entry := strings.TrimSpace(os.Getenv(browserMCPEntryPathEnv)); entry != "" {
if node := strings.TrimSpace(os.Getenv("TUTTI_APP_NODE")); node != "" {
return node, []string{entry}
}
if runtimeResolver != nil {
appRuntime, err := runtimeResolver.ResolveProfile(ctx, managedruntime.NodeStaticProfile)
if err == nil && strings.TrimSpace(appRuntime.Node) != "" {
return strings.TrimSpace(appRuntime.Node), []string{entry}
}
if err != nil {
slog.Warn(
"browser mcp managed node runtime unavailable for vendored entry",
"event", "browser_mcp.managed_node_runtime_unavailable",
"error", err,
)
}
}
return "node", []string{entry}
}
return defaultBrowserMCPCommand, append([]string(nil), defaultBrowserMCPArgs...)
}
func resolveBrowserMCPConnectionArgs(mode string) []string {
switch mode {
case "autoConnect":
return resolveAutoConnectMCPConnectionArgs()
default:
return []string{"--isolated", "--no-usage-statistics"}
}
}
func resolveBrowserUseConnectionMode(ctx context.Context, preferences PreferencesReader) string {
if preferences == nil {
return preferencesbiz.DefaultDesktopBrowserUseConnectionMode
}
stored, err := preferences.GetDesktopPreferences(ctx)
if err != nil {
return preferencesbiz.DefaultDesktopBrowserUseConnectionMode
}
mode := strings.TrimSpace(stored.BrowserUseConnectionMode)
if preferencesbiz.IsDesktopBrowserUseConnectionMode(mode) {
return mode
}
return preferencesbiz.DefaultDesktopBrowserUseConnectionMode
}