fix(browser): configure Chrome MCP existing-session launch (#71560)

This commit is contained in:
Vincent Koc
2026-04-25 05:46:39 -07:00
committed by GitHub
parent dd78b7f773
commit ab1d1a5c9e
22 changed files with 482 additions and 135 deletions

View File

@@ -8,7 +8,7 @@ function buildBridgeFromPersistedBundledRecord(
// Relocation is derived from the previous persisted registry, not a hardcoded
// table. A plugin moving from bundled to npm keeps the same plugin id; the old
// registry row is the proof that this user actually had it bundled/enabled.
if (record.origin !== "bundled" || record.enabled === false) {
if (record.origin !== "bundled" || !record.enabled) {
return null;
}
const npmSpec = record.packageInstall?.npm?.spec;
@@ -19,7 +19,7 @@ function buildBridgeFromPersistedBundledRecord(
bundledPluginId: record.pluginId,
pluginId: record.pluginId,
npmSpec,
...(record.enabledByDefault === true ? { enabledByDefault: true } : {}),
...(record.enabledByDefault ? { enabledByDefault: true } : {}),
channelIds: record.contributions.channels,
};
}

View File

@@ -780,6 +780,21 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
description:
"Per-profile Chromium user data directory for existing-session attachment through Chrome DevTools MCP. Use this for Brave, Edge, Chromium, or non-default Chrome profiles when the built-in auto-connect path would pick the wrong browser data directory on the selected host or browser node.",
},
mcpCommand: {
type: "string",
title: "Browser Profile Chrome MCP Command",
description:
"Per-profile Chrome DevTools MCP command for existing-session attachment. Defaults to npx.",
},
mcpArgs: {
type: "array",
items: {
type: "string",
},
title: "Browser Profile Chrome MCP Args",
description:
"Extra per-profile Chrome DevTools MCP arguments for existing-session attachment, such as --no-usage-statistics. Endpoint arguments here override the built-in auto-connect or browser URL selection.",
},
driver: {
anyOf: [
{
@@ -24061,6 +24076,16 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
help: "Per-profile Chromium user data directory for existing-session attachment through Chrome DevTools MCP. Use this for Brave, Edge, Chromium, or non-default Chrome profiles when the built-in auto-connect path would pick the wrong browser data directory on the selected host or browser node.",
tags: ["storage"],
},
"browser.profiles.*.mcpCommand": {
label: "Browser Profile Chrome MCP Command",
help: "Per-profile Chrome DevTools MCP command for existing-session attachment. Defaults to npx.",
tags: ["storage"],
},
"browser.profiles.*.mcpArgs": {
label: "Browser Profile Chrome MCP Args",
help: "Extra per-profile Chrome DevTools MCP arguments for existing-session attachment, such as --no-usage-statistics. Endpoint arguments here override the built-in auto-connect or browser URL selection.",
tags: ["storage"],
},
"browser.profiles.*.driver": {
label: "Browser Profile Driver",
help: 'Per-profile browser driver mode. Use "openclaw" (or legacy "clawd") for CDP-based profiles, or use "existing-session" for Chrome DevTools MCP attachment on the selected host or browser node.',

View File

@@ -286,6 +286,10 @@ export const FIELD_HELP: Record<string, string> = {
"Per-profile CDP websocket URL used for explicit remote browser routing by profile name. Use this when profile connections terminate on remote hosts or tunnels.",
"browser.profiles.*.userDataDir":
"Per-profile Chromium user data directory for existing-session attachment through Chrome DevTools MCP. Use this for Brave, Edge, Chromium, or non-default Chrome profiles when the built-in auto-connect path would pick the wrong browser data directory on the selected host or browser node.",
"browser.profiles.*.mcpCommand":
"Per-profile Chrome DevTools MCP command for existing-session attachment. Defaults to npx.",
"browser.profiles.*.mcpArgs":
"Extra per-profile Chrome DevTools MCP arguments for existing-session attachment, such as --no-usage-statistics. Endpoint arguments here override the built-in auto-connect or browser URL selection.",
"browser.profiles.*.driver":
'Per-profile browser driver mode. Use "openclaw" (or legacy "clawd") for CDP-based profiles, or use "existing-session" for Chrome DevTools MCP attachment on the selected host or browser node.',
"browser.profiles.*.headless":

View File

@@ -154,6 +154,8 @@ export const FIELD_LABELS: Record<string, string> = {
"browser.profiles.*.cdpPort": "Browser Profile CDP Port",
"browser.profiles.*.cdpUrl": "Browser Profile CDP URL",
"browser.profiles.*.userDataDir": "Browser Profile User Data Dir",
"browser.profiles.*.mcpCommand": "Browser Profile Chrome MCP Command",
"browser.profiles.*.mcpArgs": "Browser Profile Chrome MCP Args",
"browser.profiles.*.driver": "Browser Profile Driver",
"browser.profiles.*.headless": "Browser Profile Headless Mode",
"browser.profiles.*.attachOnly": "Browser Profile Attach-only Mode",

View File

@@ -5,6 +5,10 @@ export type BrowserProfileConfig = {
cdpUrl?: string;
/** Explicit user data directory for existing-session Chrome MCP attachment. */
userDataDir?: string;
/** Override the Chrome MCP command for existing-session profiles. */
mcpCommand?: string;
/** Extra Chrome MCP arguments for existing-session profiles. */
mcpArgs?: string[];
/** Profile driver (default: openclaw). */
driver?: "openclaw" | "clawd" | "existing-session";
/** If true, launch this profile in headless mode. Falls back to browser.headless. */

View File

@@ -425,6 +425,8 @@ export const OpenClawSchema = z
cdpPort: z.number().int().min(1).max(65535).optional(),
cdpUrl: z.string().optional(),
userDataDir: z.string().optional(),
mcpCommand: z.string().optional(),
mcpArgs: z.array(z.string()).optional(),
driver: z
.union([z.literal("openclaw"), z.literal("clawd"), z.literal("existing-session")])
.optional(),

View File

@@ -222,6 +222,10 @@ function pathEndsWithSegment(params: {
return Boolean(value && segment && (value === segment || value.endsWith(`/${segment}`)));
}
function bundledExtensionPathSegment(bundledDirName: string): string {
return ["extensions", bundledDirName].join("/");
}
function isBridgeBundledPathRecord(params: {
bridge: ExternalizedBundledPluginBridge;
bundledLocalPath?: string;
@@ -242,12 +246,12 @@ function isBridgeBundledPathRecord(params: {
return (
pathEndsWithSegment({
value: params.record.sourcePath,
segment: `extensions/${bundledDirName}`,
segment: bundledExtensionPathSegment(bundledDirName),
env: params.env,
}) ||
pathEndsWithSegment({
value: params.record.installPath,
segment: `extensions/${bundledDirName}`,
segment: bundledExtensionPathSegment(bundledDirName),
env: params.env,
})
);
@@ -262,7 +266,7 @@ function removeBridgeBundledLoadPaths(params: {
params.loadPaths.removeMatching((entry) =>
pathEndsWithSegment({
value: entry,
segment: `extensions/${bundledDirName}`,
segment: bundledExtensionPathSegment(bundledDirName),
env: params.env,
}),
);
@@ -896,9 +900,6 @@ export async function syncPluginsForUpdateChannel(params: {
installs = next.plugins?.installs ?? {};
changed = true;
}
if (bundledInfo?.localPath) {
loadHelpers.removePath(bundledInfo.localPath);
}
removeBridgeBundledLoadPaths({ bridge, loadPaths: loadHelpers, env });
continue;
}
@@ -907,7 +908,7 @@ export async function syncPluginsForUpdateChannel(params: {
existing &&
!isBridgeBundledPathRecord({
bridge,
bundledLocalPath: bundledInfo?.localPath,
bundledLocalPath: undefined,
record: existing.record,
env,
})
@@ -947,9 +948,6 @@ export async function syncPluginsForUpdateChannel(params: {
...buildNpmResolutionInstallFields(result.npmResolution),
});
installs = next.plugins?.installs ?? {};
if (bundledInfo?.localPath) {
loadHelpers.removePath(bundledInfo.localPath);
}
if (existing?.record.sourcePath) {
loadHelpers.removePath(existing.record.sourcePath);
}