mirror of https://github.com/openclaw/openclaw.git
browser: drop chrome-relay auto-creation, simplify to user profile only (#46596)
Merged via squash.
Prepared head SHA: 74becc8f7d
Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com>
Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com>
Reviewed-by: @odysseus0
This commit is contained in:
parent
39b4185d0b
commit
b1d8737017
|
|
@ -23,6 +23,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Agents/usage tracking: stop forcing `supportsUsageInStreaming: false` on non-native openai-completions endpoints so providers like DashScope, DeepSeek, and other OpenAI-compatible backends report token usage and cost instead of showing all zeros. (#46142)
|
||||
- Node/startup: remove leftover debug `console.log("node host PATH: ...")` that printed the resolved PATH on every `openclaw node run` invocation. (#46411)
|
||||
- Control UI/dashboard: preserve structured gateway shutdown reasons across restart disconnects so config-triggered restarts no longer fall back to `disconnected (1006): no reason`. (#46532) Thanks @vincentkoc.
|
||||
- Browser/profiles: drop the auto-created `chrome-relay` browser profile; users who need the Chrome extension relay must now create their own profile via `openclaw browser create-profile`. (#45777) Thanks @odysseus0.
|
||||
|
||||
## 2026.3.13
|
||||
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ Look for:
|
|||
|
||||
- Valid browser executable path.
|
||||
- CDP profile reachability.
|
||||
- Extension relay tab attachment for `profile="chrome-relay"`.
|
||||
- Extension relay tab attachment (if an extension relay profile is configured).
|
||||
|
||||
Common signatures:
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ Note, selecting 'chromium-browser' instead of 'chromium'
|
|||
chromium-browser is already the newest version (2:1snap1-0ubuntu2).
|
||||
```
|
||||
|
||||
This is NOT a real browser — it's just a wrapper.
|
||||
This is NOT a real browser - it's just a wrapper.
|
||||
|
||||
### Solution 1: Install Google Chrome (Recommended)
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ Notes:
|
|||
|
||||
### Problem: "Chrome extension relay is running, but no tab is connected"
|
||||
|
||||
You’re using the `chrome-relay` profile (extension relay). It expects the OpenClaw
|
||||
You're using an extension relay profile. It expects the OpenClaw
|
||||
browser extension to be attached to a live tab.
|
||||
|
||||
Fix options:
|
||||
|
|
|
|||
|
|
@ -62,19 +62,14 @@ After upgrading OpenClaw:
|
|||
|
||||
## Use it (set gateway token once)
|
||||
|
||||
OpenClaw ships with a built-in browser profile named `chrome-relay` that targets the extension relay on the default port.
|
||||
To use the extension relay, create a browser profile for it:
|
||||
|
||||
Before first attach, open extension Options and set:
|
||||
|
||||
- `Port` (default `18792`)
|
||||
- `Gateway token` (must match `gateway.auth.token` / `OPENCLAW_GATEWAY_TOKEN`)
|
||||
|
||||
Use it:
|
||||
|
||||
- CLI: `openclaw browser --browser-profile chrome-relay tabs`
|
||||
- Agent tool: `browser` with `profile="chrome-relay"`
|
||||
|
||||
If you want a different name or a different relay port, create your own profile:
|
||||
Then create a profile:
|
||||
|
||||
```bash
|
||||
openclaw browser create-profile \
|
||||
|
|
@ -84,6 +79,11 @@ openclaw browser create-profile \
|
|||
--color "#00AA00"
|
||||
```
|
||||
|
||||
Use it:
|
||||
|
||||
- CLI: `openclaw browser --browser-profile my-chrome tabs`
|
||||
- Agent tool: `browser` with `profile="my-chrome"`
|
||||
|
||||
### Custom Gateway ports
|
||||
|
||||
If you're using a custom gateway port, the extension relay port is automatically derived:
|
||||
|
|
|
|||
|
|
@ -157,11 +157,9 @@ describe("createOpenClawCodingTools", () => {
|
|||
expect(schema.type).toBe("object");
|
||||
expect(schema.anyOf).toBeUndefined();
|
||||
});
|
||||
it("mentions Chrome extension relay in browser tool description", () => {
|
||||
it("mentions user browser profile in browser tool description", () => {
|
||||
const browser = createBrowserTool();
|
||||
expect(browser.description).toMatch(/Chrome extension/i);
|
||||
expect(browser.description).toMatch(/profile="user"/i);
|
||||
expect(browser.description).toMatch(/profile="chrome-relay"/i);
|
||||
});
|
||||
it("keeps browser tool schema properties after normalization", () => {
|
||||
const browser = defaultTools.find((tool) => tool.name === "browser");
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ function formatConsoleToolResult(result: {
|
|||
}
|
||||
|
||||
function isChromeStaleTargetError(profile: string | undefined, err: unknown): boolean {
|
||||
if (profile !== "chrome-relay" && profile !== "chrome") {
|
||||
if (profile !== "chrome-relay" && profile !== "chrome" && profile !== "user") {
|
||||
return false;
|
||||
}
|
||||
const msg = String(err);
|
||||
|
|
@ -314,7 +314,7 @@ export async function executeActAction(params: {
|
|||
})) as { tabs?: unknown[] }
|
||||
).tabs ?? [])
|
||||
: await browserTabs(baseUrl, { profile }).catch(() => []);
|
||||
// Some Chrome relay targetIds can go stale between snapshots and actions.
|
||||
// Some user-browser targetIds can go stale between snapshots and actions.
|
||||
// Only retry safe read-only actions, and only when exactly one tab remains attached.
|
||||
if (retryRequest && canRetryChromeActWithoutTargetId(request) && tabs.length === 1) {
|
||||
try {
|
||||
|
|
@ -334,13 +334,17 @@ export async function executeActAction(params: {
|
|||
}
|
||||
}
|
||||
if (!tabs.length) {
|
||||
// Extension relay profiles need the toolbar icon click; Chrome MCP just needs Chrome running.
|
||||
const isRelayProfile = profile === "chrome-relay" || profile === "chrome";
|
||||
throw new Error(
|
||||
"No Chrome tabs are attached via the OpenClaw Browser Relay extension. Click the toolbar icon on the tab you want to control (badge ON), then retry.",
|
||||
isRelayProfile
|
||||
? "No Chrome tabs are attached via the OpenClaw Browser Relay extension. Click the toolbar icon on the tab you want to control (badge ON), then retry."
|
||||
: `No Chrome tabs found for profile="${profile}". Make sure Chrome is running with remote debugging enabled (chrome://inspect/#remote-debugging), approve any attach prompt, and verify open tabs. Then retry.`,
|
||||
{ cause: err },
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`Chrome tab not found (stale targetId?). Run action=tabs profile="chrome-relay" and use one of the returned targetIds.`,
|
||||
`Chrome tab not found (stale targetId?). Run action=tabs profile="${profile}" and use one of the returned targetIds.`,
|
||||
{ cause: err },
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -287,9 +287,9 @@ describe("browser tool snapshot maxChars", () => {
|
|||
expect(opts?.mode).toBeUndefined();
|
||||
});
|
||||
|
||||
it("defaults to host when using profile=chrome-relay (even in sandboxed sessions)", async () => {
|
||||
it("defaults to host when using an explicit extension relay profile (even in sandboxed sessions)", async () => {
|
||||
setResolvedBrowserProfiles({
|
||||
"chrome-relay": {
|
||||
relay: {
|
||||
driver: "extension",
|
||||
cdpUrl: "http://127.0.0.1:18792",
|
||||
color: "#0066CC",
|
||||
|
|
@ -298,14 +298,14 @@ describe("browser tool snapshot maxChars", () => {
|
|||
const tool = createBrowserTool({ sandboxBridgeUrl: "http://127.0.0.1:9999" });
|
||||
await tool.execute?.("call-1", {
|
||||
action: "snapshot",
|
||||
profile: "chrome-relay",
|
||||
profile: "relay",
|
||||
snapshotFormat: "ai",
|
||||
});
|
||||
|
||||
expect(browserClientMocks.browserSnapshot).toHaveBeenCalledWith(
|
||||
undefined,
|
||||
expect.objectContaining({
|
||||
profile: "chrome-relay",
|
||||
profile: "relay",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
@ -366,12 +366,12 @@ describe("browser tool snapshot maxChars", () => {
|
|||
|
||||
it("lets the server choose snapshot format when the user does not request one", async () => {
|
||||
const tool = createBrowserTool();
|
||||
await tool.execute?.("call-1", { action: "snapshot", profile: "chrome-relay" });
|
||||
await tool.execute?.("call-1", { action: "snapshot", profile: "user" });
|
||||
|
||||
expect(browserClientMocks.browserSnapshot).toHaveBeenCalledWith(
|
||||
undefined,
|
||||
expect.objectContaining({
|
||||
profile: "chrome-relay",
|
||||
profile: "user",
|
||||
}),
|
||||
);
|
||||
const opts = browserClientMocks.browserSnapshot.mock.calls.at(-1)?.[1] as
|
||||
|
|
@ -438,21 +438,17 @@ describe("browser tool snapshot maxChars", () => {
|
|||
expect(gatewayMocks.callGatewayTool).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps chrome-relay profile on host when node proxy is available", async () => {
|
||||
it("keeps user profile on host when node proxy is available", async () => {
|
||||
mockSingleBrowserProxyNode();
|
||||
setResolvedBrowserProfiles({
|
||||
"chrome-relay": {
|
||||
driver: "extension",
|
||||
cdpUrl: "http://127.0.0.1:18792",
|
||||
color: "#0066CC",
|
||||
},
|
||||
user: { driver: "existing-session", attachOnly: true, color: "#00AA00" },
|
||||
});
|
||||
const tool = createBrowserTool();
|
||||
await tool.execute?.("call-1", { action: "status", profile: "chrome-relay" });
|
||||
await tool.execute?.("call-1", { action: "status", profile: "user" });
|
||||
|
||||
expect(browserClientMocks.browserStatus).toHaveBeenCalledWith(
|
||||
undefined,
|
||||
expect.objectContaining({ profile: "chrome-relay" }),
|
||||
expect.objectContaining({ profile: "user" }),
|
||||
);
|
||||
expect(gatewayMocks.callGatewayTool).not.toHaveBeenCalled();
|
||||
});
|
||||
|
|
@ -745,7 +741,7 @@ describe("browser tool external content wrapping", () => {
|
|||
describe("browser tool act stale target recovery", () => {
|
||||
registerBrowserToolAfterEachReset();
|
||||
|
||||
it("retries safe chrome-relay act once without targetId when exactly one tab remains", async () => {
|
||||
it("retries safe user-browser act once without targetId when exactly one tab remains", async () => {
|
||||
browserActionsMocks.browserAct
|
||||
.mockRejectedValueOnce(new Error("404: tab not found"))
|
||||
.mockResolvedValueOnce({ ok: true });
|
||||
|
|
@ -754,7 +750,7 @@ describe("browser tool act stale target recovery", () => {
|
|||
const tool = createBrowserTool();
|
||||
const result = await tool.execute?.("call-1", {
|
||||
action: "act",
|
||||
profile: "chrome-relay",
|
||||
profile: "user",
|
||||
request: {
|
||||
kind: "hover",
|
||||
targetId: "stale-tab",
|
||||
|
|
@ -767,18 +763,18 @@ describe("browser tool act stale target recovery", () => {
|
|||
1,
|
||||
undefined,
|
||||
expect.objectContaining({ targetId: "stale-tab", kind: "hover", ref: "btn-1" }),
|
||||
expect.objectContaining({ profile: "chrome-relay" }),
|
||||
expect.objectContaining({ profile: "user" }),
|
||||
);
|
||||
expect(browserActionsMocks.browserAct).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
undefined,
|
||||
expect.not.objectContaining({ targetId: expect.anything() }),
|
||||
expect.objectContaining({ profile: "chrome-relay" }),
|
||||
expect.objectContaining({ profile: "user" }),
|
||||
);
|
||||
expect(result?.details).toMatchObject({ ok: true });
|
||||
});
|
||||
|
||||
it("does not retry mutating chrome-relay act requests without targetId", async () => {
|
||||
it("does not retry mutating user-browser act requests without targetId", async () => {
|
||||
browserActionsMocks.browserAct.mockRejectedValueOnce(new Error("404: tab not found"));
|
||||
browserClientMocks.browserTabs.mockResolvedValueOnce([{ targetId: "only-tab" }]);
|
||||
|
||||
|
|
@ -786,14 +782,14 @@ describe("browser tool act stale target recovery", () => {
|
|||
await expect(
|
||||
tool.execute?.("call-1", {
|
||||
action: "act",
|
||||
profile: "chrome-relay",
|
||||
profile: "user",
|
||||
request: {
|
||||
kind: "click",
|
||||
targetId: "stale-tab",
|
||||
ref: "btn-1",
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(/Run action=tabs profile="chrome-relay"/i);
|
||||
).rejects.toThrow(/Run action=tabs profile="user"/i);
|
||||
|
||||
expect(browserActionsMocks.browserAct).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -293,10 +293,6 @@ function shouldPreferHostForProfile(profileName: string | undefined) {
|
|||
return capabilities.requiresRelay || capabilities.usesChromeMcp;
|
||||
}
|
||||
|
||||
function isHostOnlyProfileName(profileName: string | undefined) {
|
||||
return profileName === "user" || profileName === "chrome-relay";
|
||||
}
|
||||
|
||||
export function createBrowserTool(opts?: {
|
||||
sandboxBridgeUrl?: string;
|
||||
allowHostControl?: boolean;
|
||||
|
|
@ -311,11 +307,8 @@ export function createBrowserTool(opts?: {
|
|||
description: [
|
||||
"Control the browser via OpenClaw's browser control server (status/start/stop/profiles/tabs/open/snapshot/screenshot/actions).",
|
||||
"Browser choice: omit profile by default for the isolated OpenClaw-managed browser (`openclaw`).",
|
||||
'For the logged-in user browser on the local host, prefer profile="user". Use it only when existing logins/cookies matter and the user is present to click/approve any browser attach prompt.',
|
||||
'Use profile="chrome-relay" only for the Chrome extension / Browser Relay / toolbar-button attach-tab flow, or when the user explicitly asks for the extension relay.',
|
||||
'If the user mentions the Chrome extension / Browser Relay / toolbar button / “attach tab”, ALWAYS prefer profile="chrome-relay". Otherwise prefer profile="user" over the extension relay for user-browser work.',
|
||||
'For the logged-in user browser on the local host, use profile="user". Chrome must be running with remote debugging enabled (chrome://inspect/#remote-debugging). The user must approve the browser attach prompt. Use only when existing logins/cookies matter and the user is present.',
|
||||
'When a node-hosted browser proxy is available, the tool may auto-route to it. Pin a node with node=<id|name> or target="node".',
|
||||
'User-browser flows need user interaction: profile="user" may require approving a browser attach prompt; profile="chrome-relay" needs the user to click the OpenClaw Browser Relay toolbar icon on the tab (badge ON). If user presence is unclear, ask first.',
|
||||
"When using refs from snapshot (e.g. e12), keep the same tab: prefer passing targetId from the snapshot response into subsequent actions (act/click/type/etc).",
|
||||
'For stable, self-resolving refs across calls, use snapshot with refs="aria" (Playwright aria-ref ids). Default refs="role" are role+name-based.',
|
||||
"Use snapshot+act for UI automation. Avoid act:wait by default; use only in exceptional cases when no reliable UI state exists.",
|
||||
|
|
@ -333,7 +326,9 @@ export function createBrowserTool(opts?: {
|
|||
if (requestedNode && target && target !== "node") {
|
||||
throw new Error('node is only supported with target="node".');
|
||||
}
|
||||
if (isHostOnlyProfileName(profile)) {
|
||||
// User-browser profiles (existing-session, extension relay) are host-only.
|
||||
const isUserBrowserProfile = shouldPreferHostForProfile(profile);
|
||||
if (isUserBrowserProfile) {
|
||||
if (requestedNode || target === "node") {
|
||||
throw new Error(`profile="${profile}" only supports the local host browser.`);
|
||||
}
|
||||
|
|
@ -342,11 +337,10 @@ export function createBrowserTool(opts?: {
|
|||
`profile="${profile}" cannot use the sandbox browser; use target="host" or omit target.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!target && !requestedNode && shouldPreferHostForProfile(profile)) {
|
||||
// Local host user-browser profiles should not silently bind to sandbox/node browsers.
|
||||
if (!target && !requestedNode) {
|
||||
target = "host";
|
||||
}
|
||||
}
|
||||
|
||||
const nodeTarget = await resolveBrowserNodeTarget({
|
||||
requestedNode: requestedNode ?? undefined,
|
||||
|
|
|
|||
|
|
@ -266,11 +266,6 @@ describe("browser server-context listKnownProfileNames", () => {
|
|||
]),
|
||||
};
|
||||
|
||||
expect(listKnownProfileNames(state).toSorted()).toEqual([
|
||||
"chrome-relay",
|
||||
"openclaw",
|
||||
"stale-removed",
|
||||
"user",
|
||||
]);
|
||||
expect(listKnownProfileNames(state).toSorted()).toEqual(["openclaw", "stale-removed", "user"]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -27,10 +27,8 @@ describe("browser config", () => {
|
|||
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);
|
||||
expect(chromeRelay?.cdpUrl).toBe("http://127.0.0.1:18792");
|
||||
// chrome-relay is no longer auto-created
|
||||
expect(resolveProfile(resolved, "chrome-relay")).toBe(null);
|
||||
expect(resolved.remoteCdpTimeoutMs).toBe(1500);
|
||||
expect(resolved.remoteCdpHandshakeTimeoutMs).toBe(3000);
|
||||
});
|
||||
|
|
@ -39,10 +37,7 @@ describe("browser config", () => {
|
|||
withEnv({ OPENCLAW_GATEWAY_PORT: "19001" }, () => {
|
||||
const resolved = resolveBrowserConfig(undefined);
|
||||
expect(resolved.controlPort).toBe(19003);
|
||||
const chromeRelay = resolveProfile(resolved, "chrome-relay");
|
||||
expect(chromeRelay?.driver).toBe("extension");
|
||||
expect(chromeRelay?.cdpPort).toBe(19004);
|
||||
expect(chromeRelay?.cdpUrl).toBe("http://127.0.0.1:19004");
|
||||
expect(resolveProfile(resolved, "chrome-relay")).toBe(null);
|
||||
|
||||
const openclaw = resolveProfile(resolved, "openclaw");
|
||||
expect(openclaw?.cdpPort).toBe(19012);
|
||||
|
|
@ -54,10 +49,7 @@ describe("browser config", () => {
|
|||
withEnv({ OPENCLAW_GATEWAY_PORT: undefined }, () => {
|
||||
const resolved = resolveBrowserConfig(undefined, { gateway: { port: 19011 } });
|
||||
expect(resolved.controlPort).toBe(19013);
|
||||
const chromeRelay = resolveProfile(resolved, "chrome-relay");
|
||||
expect(chromeRelay?.driver).toBe("extension");
|
||||
expect(chromeRelay?.cdpPort).toBe(19014);
|
||||
expect(chromeRelay?.cdpUrl).toBe("http://127.0.0.1:19014");
|
||||
expect(resolveProfile(resolved, "chrome-relay")).toBe(null);
|
||||
|
||||
const openclaw = resolveProfile(resolved, "openclaw");
|
||||
expect(openclaw?.cdpPort).toBe(19022);
|
||||
|
|
@ -228,16 +220,6 @@ describe("browser config", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("does not add the built-in chrome-relay profile if the derived relay port is already used", () => {
|
||||
const resolved = resolveBrowserConfig({
|
||||
profiles: {
|
||||
openclaw: { cdpPort: 18792, color: "#FF4500" },
|
||||
},
|
||||
});
|
||||
expect(resolveProfile(resolved, "chrome-relay")).toBe(null);
|
||||
expect(resolved.defaultProfile).toBe("openclaw");
|
||||
});
|
||||
|
||||
it("defaults extraArgs to empty array when not provided", () => {
|
||||
const resolved = resolveBrowserConfig(undefined);
|
||||
expect(resolved.extraArgs).toEqual([]);
|
||||
|
|
@ -326,6 +308,7 @@ describe("browser config", () => {
|
|||
const resolved = resolveBrowserConfig({
|
||||
profiles: {
|
||||
"chrome-live": { driver: "existing-session", attachOnly: true, color: "#00AA00" },
|
||||
relay: { driver: "extension", cdpUrl: "http://127.0.0.1:18792", color: "#0066CC" },
|
||||
work: { cdpPort: 18801, color: "#0066CC" },
|
||||
},
|
||||
});
|
||||
|
|
@ -336,7 +319,7 @@ describe("browser config", () => {
|
|||
const managed = resolveProfile(resolved, "openclaw")!;
|
||||
expect(getBrowserProfileCapabilities(managed).usesChromeMcp).toBe(false);
|
||||
|
||||
const extension = resolveProfile(resolved, "chrome-relay")!;
|
||||
const extension = resolveProfile(resolved, "relay")!;
|
||||
expect(getBrowserProfileCapabilities(extension).usesChromeMcp).toBe(false);
|
||||
|
||||
const work = resolveProfile(resolved, "work")!;
|
||||
|
|
@ -377,17 +360,17 @@ describe("browser config", () => {
|
|||
it("explicit defaultProfile config overrides defaults in headless mode", () => {
|
||||
const resolved = resolveBrowserConfig({
|
||||
headless: true,
|
||||
defaultProfile: "chrome-relay",
|
||||
defaultProfile: "user",
|
||||
});
|
||||
expect(resolved.defaultProfile).toBe("chrome-relay");
|
||||
expect(resolved.defaultProfile).toBe("user");
|
||||
});
|
||||
|
||||
it("explicit defaultProfile config overrides defaults in noSandbox mode", () => {
|
||||
const resolved = resolveBrowserConfig({
|
||||
noSandbox: true,
|
||||
defaultProfile: "chrome-relay",
|
||||
defaultProfile: "user",
|
||||
});
|
||||
expect(resolved.defaultProfile).toBe("chrome-relay");
|
||||
expect(resolved.defaultProfile).toBe("user");
|
||||
});
|
||||
|
||||
it("allows custom profile as default even in headless mode", () => {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
DEFAULT_BROWSER_DEFAULT_PROFILE_NAME,
|
||||
DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME,
|
||||
} from "./constants.js";
|
||||
import { CDP_PORT_RANGE_START, getUsedPorts } from "./profiles.js";
|
||||
import { CDP_PORT_RANGE_START } from "./profiles.js";
|
||||
|
||||
export type ResolvedBrowserConfig = {
|
||||
enabled: boolean;
|
||||
|
|
@ -198,36 +198,6 @@ function ensureDefaultUserBrowserProfile(
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a built-in "chrome-relay" profile exists for the Chrome extension relay.
|
||||
*
|
||||
* Note: this is an OpenClaw browser profile (routing config), not a Chrome user profile.
|
||||
* It points at the local relay CDP endpoint (controlPort + 1).
|
||||
*/
|
||||
function ensureDefaultChromeRelayProfile(
|
||||
profiles: Record<string, BrowserProfileConfig>,
|
||||
controlPort: number,
|
||||
): Record<string, BrowserProfileConfig> {
|
||||
const result = { ...profiles };
|
||||
if (result["chrome-relay"]) {
|
||||
return result;
|
||||
}
|
||||
const relayPort = controlPort + 1;
|
||||
if (!Number.isFinite(relayPort) || relayPort <= 0 || relayPort > 65535) {
|
||||
return result;
|
||||
}
|
||||
// Avoid adding the built-in profile if the derived relay port is already used by another profile
|
||||
// (legacy single-profile configs may use controlPort+1 for openclaw/openclaw CDP).
|
||||
if (getUsedPorts(result).has(relayPort)) {
|
||||
return result;
|
||||
}
|
||||
result["chrome-relay"] = {
|
||||
driver: "extension",
|
||||
cdpUrl: `http://127.0.0.1:${relayPort}`,
|
||||
color: "#00AA00",
|
||||
};
|
||||
return result;
|
||||
}
|
||||
export function resolveBrowserConfig(
|
||||
cfg: BrowserConfig | undefined,
|
||||
rootConfig?: OpenClawConfig,
|
||||
|
|
@ -287,8 +257,7 @@ export function resolveBrowserConfig(
|
|||
const legacyCdpPort = rawCdpUrl ? cdpInfo.port : undefined;
|
||||
const isWsUrl = cdpInfo.parsed.protocol === "ws:" || cdpInfo.parsed.protocol === "wss:";
|
||||
const legacyCdpUrl = rawCdpUrl && isWsUrl ? cdpInfo.normalized : undefined;
|
||||
const profiles = ensureDefaultChromeRelayProfile(
|
||||
ensureDefaultUserBrowserProfile(
|
||||
const profiles = ensureDefaultUserBrowserProfile(
|
||||
ensureDefaultProfile(
|
||||
cfg?.profiles,
|
||||
defaultColor,
|
||||
|
|
@ -296,8 +265,6 @@ export function resolveBrowserConfig(
|
|||
cdpPortRangeStart,
|
||||
legacyCdpUrl,
|
||||
),
|
||||
),
|
||||
controlPort,
|
||||
);
|
||||
const cdpProtocol = cdpInfo.parsed.protocol === "https:" ? "https" : "http";
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,15 @@ import { resolveBrowserConfig, resolveProfile } from "../config.js";
|
|||
import { resolveSnapshotPlan } from "./agent.snapshot.plan.js";
|
||||
|
||||
describe("resolveSnapshotPlan", () => {
|
||||
it("defaults chrome-relay snapshots to aria when format is omitted", () => {
|
||||
const resolved = resolveBrowserConfig({});
|
||||
const profile = resolveProfile(resolved, "chrome-relay");
|
||||
it("defaults extension relay snapshots to aria when format is omitted", () => {
|
||||
const resolved = resolveBrowserConfig({
|
||||
profiles: {
|
||||
relay: { driver: "extension", cdpUrl: "http://127.0.0.1:18792", color: "#0066CC" },
|
||||
},
|
||||
});
|
||||
const profile = resolveProfile(resolved, "relay");
|
||||
expect(profile).toBeTruthy();
|
||||
expect(profile?.driver).toBe("extension");
|
||||
|
||||
const plan = resolveSnapshotPlan({
|
||||
profile: profile as NonNullable<typeof profile>,
|
||||
|
|
|
|||
|
|
@ -681,8 +681,7 @@ export function attachGatewayWsMessageHandler(params: {
|
|||
hasBrowserOriginHeader,
|
||||
sharedAuthOk,
|
||||
authMethod,
|
||||
}) ||
|
||||
shouldSkipControlUiPairing(controlUiAuthPolicy, role, trustedProxyAuthOk);
|
||||
}) || shouldSkipControlUiPairing(controlUiAuthPolicy, role, trustedProxyAuthOk);
|
||||
if (device && devicePublicKey && !skipPairing) {
|
||||
const formatAuditList = (items: string[] | undefined): string => {
|
||||
if (!items || items.length === 0) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue