feat(browser): support headless MCP profile resolution

This commit is contained in:
Vincent Koc
2026-03-14 01:26:39 -07:00
parent a837412a37
commit 00ae84c2e5
6 changed files with 34 additions and 9 deletions

View File

@@ -26,6 +26,7 @@ describe("browser config", () => {
expect(user?.driver).toBe("existing-session");
expect(user?.cdpPort).toBe(0);
expect(user?.cdpUrl).toBe("");
expect(user?.mcpTargetUrl).toBeUndefined();
const chromeRelay = resolveProfile(resolved, "chrome-relay");
expect(chromeRelay?.driver).toBe("extension");
expect(chromeRelay?.cdpPort).toBe(18792);
@@ -121,6 +122,24 @@ describe("browser config", () => {
expect(profile?.cdpIsLoopback).toBe(false);
});
it("supports MCP browser URLs for existing-session profiles", () => {
const resolved = resolveBrowserConfig({
profiles: {
user: {
driver: "existing-session",
cdpUrl: "http://127.0.0.1:9222",
color: "#00AA00",
},
},
});
const profile = resolveProfile(resolved, "user");
expect(profile?.driver).toBe("existing-session");
expect(profile?.cdpUrl).toBe("");
expect(profile?.mcpTargetUrl).toBe("http://127.0.0.1:9222");
expect(profile?.cdpIsLoopback).toBe(true);
});
it("uses profile cdpUrl when provided", () => {
const resolved = resolveBrowserConfig({
profiles: {

View File

@@ -45,6 +45,7 @@ export type ResolvedBrowserProfile = {
cdpUrl: string;
cdpHost: string;
cdpIsLoopback: boolean;
mcpTargetUrl?: string;
color: string;
driver: "openclaw" | "extension" | "existing-session";
attachOnly: boolean;
@@ -363,13 +364,18 @@ export function resolveProfile(
: "openclaw";
if (driver === "existing-session") {
// existing-session uses Chrome MCP auto-connect; no CDP port/URL needed
const parsed = rawProfileUrl
? parseHttpUrl(rawProfileUrl, `browser.profiles.${profileName}.cdpUrl`)
: null;
// existing-session uses Chrome MCP. It can either auto-connect to a local desktop
// session or connect to a debuggable browser URL/WS endpoint when explicitly configured.
return {
name: profileName,
cdpPort: 0,
cdpUrl: "",
cdpHost: "",
cdpIsLoopback: true,
cdpHost: parsed?.parsed.hostname ?? "",
cdpIsLoopback: parsed ? isLoopbackHost(parsed.parsed.hostname) : true,
...(parsed ? { mcpTargetUrl: parsed.normalized } : {}),
color: profile.color,
driver,
attachOnly: true,

View File

@@ -41,7 +41,7 @@ export function getBrowserProfileCapabilities(
if (profile.driver === "existing-session") {
return {
mode: "local-existing-session",
isRemote: false,
isRemote: !profile.cdpIsLoopback,
usesChromeMcp: true,
requiresRelay: false,
requiresAttachedTab: false,

View File

@@ -54,12 +54,12 @@ describe("browser server-context headless implicit default profile", () => {
expect(ctx.forProfile().profile.name).toBe("openclaw");
});
it("falls back from existing-session to openclaw when no profile is specified", () => {
it("keeps existing-session as the implicit default in headless mode", () => {
const ctx = createBrowserRouteContext({
getState: () => makeState("user"),
});
expect(ctx.forProfile().profile.name).toBe("openclaw");
expect(ctx.forProfile().profile.name).toBe("user");
});
it("keeps explicit interactive profile requests unchanged in headless mode", () => {

View File

@@ -53,7 +53,7 @@ function resolveImplicitProfileName(state: BrowserServerState): string {
}
const capabilities = getBrowserProfileCapabilities(defaultProfile);
if (!capabilities.requiresRelay && !capabilities.usesChromeMcp) {
if (!capabilities.requiresRelay) {
return defaultProfileName;
}
@@ -63,7 +63,7 @@ function resolveImplicitProfileName(state: BrowserServerState): string {
}
const managedCapabilities = getBrowserProfileCapabilities(managedProfile);
if (managedCapabilities.requiresRelay || managedCapabilities.usesChromeMcp) {
if (managedCapabilities.requiresRelay) {
return defaultProfileName;
}

View File

@@ -1,7 +1,7 @@
export type BrowserProfileConfig = {
/** CDP port for this profile. Allocated once at creation, persisted permanently. */
cdpPort?: number;
/** CDP URL for this profile (use for remote Chrome). */
/** CDP URL for this profile (use for remote Chrome, or as browserUrl/wsEndpoint for existing-session MCP attach). */
cdpUrl?: string;
/** Profile driver (default: openclaw). */
driver?: "openclaw" | "clawd" | "extension" | "existing-session";