refactor: remove legacy browser bridge entrypoints

This commit is contained in:
Peter Steinberger
2026-03-26 23:09:56 +00:00
parent 4b40d4dfa8
commit d1d0887932
195 changed files with 355 additions and 420 deletions

View File

@@ -26,9 +26,6 @@ function mockToolFactory(name: string) {
vi.mock("./tools/agents-list-tool.js", () => ({
createAgentsListTool: mockToolFactory("agents_list_stub"),
}));
vi.mock("./tools/browser-tool.js", () => ({
createBrowserTool: mockToolFactory("browser_stub"),
}));
vi.mock("./tools/canvas-tool.js", () => ({
createCanvasTool: mockToolFactory("canvas_stub"),
}));

View File

@@ -4,15 +4,15 @@ import path from "node:path";
import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core";
import { Type } from "@sinclair/typebox";
import { describe, expect, it, vi } from "vitest";
import { XAI_UNSUPPORTED_SCHEMA_KEYWORDS } from "../plugin-sdk/provider-tools.js";
import { createBrowserTool } from "../plugin-sdk/browser.js";
import "./test-helpers/fast-coding-tools.js";
import { XAI_UNSUPPORTED_SCHEMA_KEYWORDS } from "../plugin-sdk/provider-tools.js";
import { applyXaiModelCompat } from "./model-compat.js";
import { createOpenClawTools } from "./openclaw-tools.js";
import { findUnsupportedSchemaKeywords } from "./pi-embedded-runner/google.js";
import { __testing, createOpenClawCodingTools } from "./pi-tools.js";
import { createOpenClawReadTool, createSandboxedReadTool } from "./pi-tools.read.js";
import { createHostSandboxFsBridge } from "./test-helpers/host-sandbox-fs-bridge.js";
import { createBrowserTool } from "./tools/browser-tool.js";
const defaultTools = createOpenClawCodingTools();
@@ -164,9 +164,8 @@ describe("createOpenClawCodingTools", () => {
expect(browser.description).toMatch(/profile="user"/i);
});
it("keeps browser tool schema properties after normalization", () => {
const browser = defaultTools.find((tool) => tool.name === "browser");
expect(browser).toBeDefined();
const parameters = browser?.parameters as {
const browser = createBrowserTool();
const parameters = browser.parameters as {
anyOf?: unknown[];
properties?: Record<string, unknown>;
required?: string[];
@@ -193,10 +192,8 @@ describe("createOpenClawCodingTools", () => {
expect(parameters.required ?? []).not.toContain("raw");
});
it("flattens anyOf-of-literals to enum for provider compatibility", () => {
const browser = defaultTools.find((tool) => tool.name === "browser");
expect(browser).toBeDefined();
const parameters = browser?.parameters as {
const browser = createBrowserTool();
const parameters = browser.parameters as {
properties?: Record<string, unknown>;
};
const action = parameters.properties?.action as

View File

@@ -1,10 +1,6 @@
import { vi } from "vitest";
import { stubTool } from "./fast-tool-stubs.js";
vi.mock("../tools/browser-tool.js", () => ({
createBrowserTool: () => stubTool("browser"),
}));
vi.mock("../tools/canvas-tool.js", () => ({
createCanvasTool: () => stubTool("canvas"),
}));

View File

@@ -1 +0,0 @@
export * from "../../../extensions/browser/src/browser-tool.actions.js";

View File

@@ -1 +0,0 @@
export * from "../../../extensions/browser/src/browser-tool.schema.js";

View File

@@ -27,7 +27,7 @@ const browserClientMocks = vi.hoisted(() => ({
browserStop: vi.fn(async (..._args: unknown[]) => ({})),
browserTabs: vi.fn(async (..._args: unknown[]): Promise<Array<Record<string, unknown>>> => []),
}));
vi.mock("../../browser/client.js", () => browserClientMocks);
vi.mock("../../../extensions/browser/src/browser/client.js", () => browserClientMocks);
const browserActionsMocks = vi.hoisted(() => ({
browserAct: vi.fn(async () => ({ ok: true })),
@@ -48,7 +48,7 @@ const browserActionsMocks = vi.hoisted(() => ({
browserPdfSave: vi.fn(async () => ({ ok: true, path: "/tmp/test.pdf" })),
browserScreenshotAction: vi.fn(async () => ({ ok: true, path: "/tmp/test.png" })),
}));
vi.mock("../../browser/client-actions.js", () => browserActionsMocks);
vi.mock("../../../extensions/browser/src/browser/client-actions.js", () => browserActionsMocks);
const browserConfigMocks = vi.hoisted(() => ({
resolveBrowserConfig: vi.fn(() => ({
@@ -89,7 +89,7 @@ const browserConfigMocks = vi.hoisted(() => ({
};
}),
}));
vi.mock("../../browser/config.js", () => browserConfigMocks);
vi.mock("../../../extensions/browser/src/browser/config.js", () => browserConfigMocks);
const nodesUtilsMocks = vi.hoisted(() => ({
listNodes: vi.fn(async (..._args: unknown[]): Promise<Array<Record<string, unknown>>> => []),
@@ -119,7 +119,10 @@ const sessionTabRegistryMocks = vi.hoisted(() => ({
trackSessionBrowserTab: vi.fn(),
untrackSessionBrowserTab: vi.fn(),
}));
vi.mock("../../browser/session-tab-registry.js", () => sessionTabRegistryMocks);
vi.mock(
"../../../extensions/browser/src/browser/session-tab-registry.js",
() => sessionTabRegistryMocks,
);
const toolCommonMocks = vi.hoisted(() => ({
imageResultFromFile: vi.fn(),
@@ -132,9 +135,12 @@ vi.mock("./common.js", async () => {
};
});
import { DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "../../browser/constants.js";
import { __testing as browserToolActionsTesting } from "./browser-tool.actions.js";
import { __testing as browserToolTesting, createBrowserTool } from "./browser-tool.js";
import { __testing as browserToolActionsTesting } from "../../../extensions/browser/src/browser-tool.actions.js";
import {
__testing as browserToolTesting,
createBrowserTool,
} from "../../../extensions/browser/src/browser-tool.js";
import { DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "../../../extensions/browser/src/browser/constants.js";
function mockSingleBrowserProxyNode() {
nodesUtilsMocks.listNodes.mockResolvedValue([

View File

@@ -1 +0,0 @@
export * from "../../../extensions/browser/src/browser-tool.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/bridge-auth-registry.js";

View File

@@ -1,10 +1,13 @@
import { afterEach, describe, expect, it } from "vitest";
import { startBrowserBridgeServer, stopBrowserBridgeServer } from "./bridge-server.js";
import type { ResolvedBrowserConfig } from "./config.js";
import {
startBrowserBridgeServer,
stopBrowserBridgeServer,
} from "../../extensions/browser/src/browser/bridge-server.js";
import type { ResolvedBrowserConfig } from "../../extensions/browser/src/browser/config.js";
import {
DEFAULT_OPENCLAW_BROWSER_COLOR,
DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME,
} from "./constants.js";
} from "../../extensions/browser/src/browser/constants.js";
function buildResolvedConfig(): ResolvedBrowserConfig {
return {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/bridge-server.js";

View File

@@ -3,14 +3,17 @@ import {
appendCdpPath,
getHeadersWithAuth,
normalizeCdpHttpBaseForJsonEndpoints,
} from "./cdp.helpers.js";
import { __test } from "./client-fetch.js";
import { resolveBrowserConfig, resolveProfile } from "./config.js";
import { shouldRejectBrowserMutation } from "./csrf.js";
import { toBoolean } from "./routes/utils.js";
import type { BrowserServerState } from "./server-context.js";
import { listKnownProfileNames } from "./server-context.js";
import { resolveTargetIdFromTabs } from "./target-id.js";
} from "../../extensions/browser/src/browser/cdp.helpers.js";
import { __test } from "../../extensions/browser/src/browser/client-fetch.js";
import {
resolveBrowserConfig,
resolveProfile,
} from "../../extensions/browser/src/browser/config.js";
import { shouldRejectBrowserMutation } from "../../extensions/browser/src/browser/csrf.js";
import { toBoolean } from "../../extensions/browser/src/browser/routes/utils.js";
import type { BrowserServerState } from "../../extensions/browser/src/browser/server-context.js";
import { listKnownProfileNames } from "../../extensions/browser/src/browser/server-context.js";
import { resolveTargetIdFromTabs } from "../../extensions/browser/src/browser/target-id.js";
describe("toBoolean", () => {
it("parses yes/no and 1/0", () => {

View File

@@ -6,7 +6,7 @@ import {
hasProxyEnv,
withNoProxyForCdpUrl,
withNoProxyForLocalhost,
} from "./cdp-proxy-bypass.js";
} from "../../extensions/browser/src/browser/cdp-proxy-bypass.js";
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -202,7 +202,8 @@ describe("cdp-proxy-bypass", () => {
describe("withNoProxyForLocalhost concurrency", () => {
it("does not leak NO_PROXY when called concurrently", async () => {
await withIsolatedNoProxyEnv(async () => {
const { withNoProxyForLocalhost } = await import("./cdp-proxy-bypass.js");
const { withNoProxyForLocalhost } =
await import("../../extensions/browser/src/browser/cdp-proxy-bypass.js");
// Simulate concurrent calls
const callA = withNoProxyForLocalhost(async () => {
@@ -229,7 +230,8 @@ describe("withNoProxyForLocalhost concurrency", () => {
describe("withNoProxyForLocalhost reverse exit order", () => {
it("restores NO_PROXY when first caller exits before second", async () => {
await withIsolatedNoProxyEnv(async () => {
const { withNoProxyForLocalhost } = await import("./cdp-proxy-bypass.js");
const { withNoProxyForLocalhost } =
await import("../../extensions/browser/src/browser/cdp-proxy-bypass.js");
// Call A enters first, exits first (short task)
// Call B enters second, exits last (long task)
@@ -259,7 +261,8 @@ describe("withNoProxyForLocalhost preserves user-configured NO_PROXY", () => {
process.env.HTTP_PROXY = "http://proxy:8080";
try {
const { withNoProxyForLocalhost } = await import("./cdp-proxy-bypass.js");
const { withNoProxyForLocalhost } =
await import("../../extensions/browser/src/browser/cdp-proxy-bypass.js");
await withNoProxyForLocalhost(async () => {
// Should not modify since loopback is already covered

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/cdp-proxy-bypass.js";

View File

@@ -4,7 +4,7 @@ import {
PROFILE_WS_REACHABILITY_MAX_TIMEOUT_MS,
PROFILE_WS_REACHABILITY_MIN_TIMEOUT_MS,
resolveCdpReachabilityTimeouts,
} from "./cdp-timeouts.js";
} from "../../extensions/browser/src/browser/cdp-timeouts.js";
describe("resolveCdpReachabilityTimeouts", () => {
it("uses loopback defaults when timeout is omitted", () => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/cdp-timeouts.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/cdp.helpers.js";

View File

@@ -1,12 +1,17 @@
import { createServer } from "node:http";
import { afterEach, describe, expect, it, vi } from "vitest";
import { type WebSocket, WebSocketServer } from "ws";
import { isWebSocketUrl } from "../../extensions/browser/src/browser/cdp.helpers.js";
import {
createTargetViaCdp,
evaluateJavaScript,
normalizeCdpWsUrl,
snapshotAria,
} from "../../extensions/browser/src/browser/cdp.js";
import { parseHttpUrl } from "../../extensions/browser/src/browser/config.js";
import { InvalidBrowserNavigationUrlError } from "../../extensions/browser/src/browser/navigation-guard.js";
import { SsrFBlockedError } from "../infra/net/ssrf.js";
import { rawDataToString } from "../infra/ws.js";
import { isWebSocketUrl } from "./cdp.helpers.js";
import { createTargetViaCdp, evaluateJavaScript, normalizeCdpWsUrl, snapshotAria } from "./cdp.js";
import { parseHttpUrl } from "./config.js";
import { InvalidBrowserNavigationUrlError } from "./navigation-guard.js";
describe("cdp", () => {
let httpServer: ReturnType<typeof createServer> | null = null;

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/cdp.js";

View File

@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import {
buildAiSnapshotFromChromeMcpSnapshot,
flattenChromeMcpSnapshotToAriaNodes,
} from "./chrome-mcp.snapshot.js";
} from "../../extensions/browser/src/browser/chrome-mcp.snapshot.js";
const snapshot = {
id: "root",

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/chrome-mcp.snapshot.js";

View File

@@ -6,7 +6,7 @@ import {
openChromeMcpTab,
resetChromeMcpSessionsForTest,
setChromeMcpSessionFactoryForTest,
} from "./chrome-mcp.js";
} from "../../extensions/browser/src/browser/chrome-mcp.js";
type ToolCall = {
name: string;

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/chrome-mcp.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/chrome-user-data-dir.test-harness.js";

View File

@@ -25,7 +25,7 @@ import * as fs from "node:fs";
import os from "node:os";
async function loadResolveBrowserExecutableForPlatform() {
const mod = await import("./chrome.executables.js");
const mod = await import("../../extensions/browser/src/browser/chrome.executables.js");
return mod.resolveBrowserExecutableForPlatform;
}

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/chrome.executables.js";

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { buildOpenClawChromeLaunchArgs } from "./chrome.js";
import { buildOpenClawChromeLaunchArgs } from "../../extensions/browser/src/browser/chrome.js";
describe("browser chrome launch args", () => {
it("does not force an about:blank tab at startup", () => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/chrome.profile-decoration.js";

View File

@@ -15,11 +15,11 @@ import {
isChromeReachable,
resolveBrowserExecutableForPlatform,
stopOpenClawChrome,
} from "./chrome.js";
} from "../../extensions/browser/src/browser/chrome.js";
import {
DEFAULT_OPENCLAW_BROWSER_COLOR,
DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME,
} from "./constants.js";
} from "../../extensions/browser/src/browser/constants.js";
type StopChromeTarget = Parameters<typeof stopOpenClawChrome>[0];

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/chrome.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/client-actions-core.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/client-actions-observe.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/client-actions-state.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/client-actions-types.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/client-actions-url.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/client-actions.js";

View File

@@ -1,5 +1,5 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { BrowserDispatchResponse } from "./routes/dispatcher.js";
import type { BrowserDispatchResponse } from "../../extensions/browser/src/browser/routes/dispatcher.js";
function okDispatchResponse(): BrowserDispatchResponse {
return { status: 200, body: { ok: true } };
@@ -50,7 +50,7 @@ vi.mock("../../extensions/browser/src/browser/routes/dispatcher.js", () => ({
})),
}));
let fetchBrowserJson: typeof import("./client-fetch.js").fetchBrowserJson;
let fetchBrowserJson: typeof import("../../extensions/browser/src/browser/client-fetch.js").fetchBrowserJson;
function stubJsonFetchOk() {
const fetchMock = vi.fn<(input: RequestInfo | URL, init?: RequestInit) => Promise<Response>>(
@@ -88,7 +88,7 @@ async function expectThrownBrowserFetchError(
describe("fetchBrowserJson loopback auth", () => {
beforeAll(async () => {
vi.resetModules();
({ fetchBrowserJson } = await import("./client-fetch.js"));
({ fetchBrowserJson } = await import("../../extensions/browser/src/browser/client-fetch.js"));
});
beforeEach(() => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/client-fetch.js";

View File

@@ -7,8 +7,13 @@ import {
browserNavigate,
browserPdfSave,
browserScreenshotAction,
} from "./client-actions.js";
import { browserOpenTab, browserSnapshot, browserStatus, browserTabs } from "./client.js";
} from "../../extensions/browser/src/browser/client-actions.js";
import {
browserOpenTab,
browserSnapshot,
browserStatus,
browserTabs,
} from "../../extensions/browser/src/browser/client.js";
describe("browser client", () => {
function stubSnapshotFetch(calls: string[]) {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/client.js";

View File

@@ -1,8 +1,12 @@
import { describe, expect, it } from "vitest";
import {
resolveBrowserConfig,
resolveProfile,
shouldStartLocalBrowserServer,
} from "../../extensions/browser/src/browser/config.js";
import { getBrowserProfileCapabilities } from "../../extensions/browser/src/browser/profile-capabilities.js";
import { withEnv } from "../test-utils/env.js";
import { resolveUserPath } from "../utils.js";
import { resolveBrowserConfig, resolveProfile, shouldStartLocalBrowserServer } from "./config.js";
import { getBrowserProfileCapabilities } from "./profile-capabilities.js";
describe("browser config", () => {
it("defaults to enabled with loopback defaults and lobster-orange color", () => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/config.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/constants.js";

View File

@@ -16,7 +16,7 @@ vi.mock("../config/config.js", async (importOriginal) => {
};
});
let ensureBrowserControlAuth: typeof import("./control-auth.js").ensureBrowserControlAuth;
let ensureBrowserControlAuth: typeof import("../../extensions/browser/src/browser/control-auth.js").ensureBrowserControlAuth;
describe("ensureBrowserControlAuth", () => {
const expectExplicitModeSkipsAutoAuth = async (mode: "password" | "none") => {
@@ -49,7 +49,8 @@ describe("ensureBrowserControlAuth", () => {
beforeEach(async () => {
vi.resetModules();
({ ensureBrowserControlAuth } = await import("./control-auth.js"));
({ ensureBrowserControlAuth } =
await import("../../extensions/browser/src/browser/control-auth.js"));
vi.restoreAllMocks();
mocks.loadConfig.mockClear();
mocks.writeConfigFile.mockClear();

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { ensureBrowserControlAuth } from "../../extensions/browser/src/browser/control-auth.js";
import type { OpenClawConfig } from "../config/types.js";
import { ensureBrowserControlAuth } from "./control-auth.js";
describe("ensureBrowserControlAuth", () => {
async function expectNoAutoGeneratedAuth(cfg: OpenClawConfig): Promise<void> {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/control-auth.js";

View File

@@ -24,7 +24,7 @@ vi.mock("../config/config.js", async (importOriginal) => {
};
});
vi.mock("./config.js", () => ({
vi.mock("../../extensions/browser/src/browser/config.js", () => ({
resolveBrowserConfig: vi.fn(() => ({
enabled: true,
controlPort: 18791,
@@ -32,23 +32,24 @@ vi.mock("./config.js", () => ({
})),
}));
vi.mock("./control-auth.js", () => ({
vi.mock("../../extensions/browser/src/browser/control-auth.js", () => ({
ensureBrowserControlAuth: mocks.ensureBrowserControlAuth,
}));
vi.mock("./runtime-lifecycle.js", () => ({
vi.mock("../../extensions/browser/src/browser/runtime-lifecycle.js", () => ({
createBrowserRuntimeState: mocks.createBrowserRuntimeState,
stopBrowserRuntime: vi.fn(async () => {}),
}));
let startBrowserControlServiceFromConfig: typeof import("./control-service.js").startBrowserControlServiceFromConfig;
let startBrowserControlServiceFromConfig: typeof import("../../extensions/browser/src/browser/control-service.js").startBrowserControlServiceFromConfig;
describe("startBrowserControlServiceFromConfig", () => {
beforeEach(async () => {
mocks.ensureBrowserControlAuth.mockClear();
mocks.createBrowserRuntimeState.mockClear();
vi.resetModules();
({ startBrowserControlServiceFromConfig } = await import("./control-service.js"));
({ startBrowserControlServiceFromConfig } =
await import("../../extensions/browser/src/browser/control-service.js"));
});
it("does not start the default service when the browser plugin is disabled", async () => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/control-service.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/csrf.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/errors.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/form-fields.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/http-auth.js";

View File

@@ -1,12 +1,12 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { SsrFBlockedError, type LookupFn } from "../infra/net/ssrf.js";
import {
assertBrowserNavigationAllowed,
assertBrowserNavigationRedirectChainAllowed,
assertBrowserNavigationResultAllowed,
InvalidBrowserNavigationUrlError,
requiresInspectableBrowserNavigationRedirects,
} from "./navigation-guard.js";
} from "../../extensions/browser/src/browser/navigation-guard.js";
import { SsrFBlockedError, type LookupFn } from "../infra/net/ssrf.js";
function createLookupFn(address: string): LookupFn {
const family = address.includes(":") ? 6 : 4;

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/navigation-guard.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/output-atomic.js";

View File

@@ -8,7 +8,7 @@ import {
resolvePathWithinRoot,
resolveStrictExistingPathsWithinRoot,
resolveWritablePathWithinRoot,
} from "./paths.js";
} from "../../extensions/browser/src/browser/paths.js";
async function createFixtureRoot(): Promise<{ baseDir: string; uploadsDir: string }> {
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-browser-paths-"));

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/paths.js";

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { isDefaultBrowserPluginEnabled } from "../../extensions/browser/src/browser/plugin-enabled.js";
import type { OpenClawConfig } from "../config/config.js";
import { isDefaultBrowserPluginEnabled } from "./plugin-enabled.js";
describe("isDefaultBrowserPluginEnabled", () => {
it("defaults to enabled", () => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/plugin-enabled.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/plugin-service.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/profile-capabilities.js";

View File

@@ -1,7 +1,13 @@
import fs from "node:fs";
import path from "node:path";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { BrowserRouteContext, BrowserServerState } from "./server-context.js";
import type {
BrowserRouteContext,
BrowserServerState,
} from "../../extensions/browser/src/browser/server-context.js";
import { resolveOpenClawUserDataDir } from "../../extensions/browser/src/browser/chrome.js";
import { movePathToTrash } from "../../extensions/browser/src/browser/trash.js";
import { loadConfig, writeConfigFile } from "../../extensions/browser/src/config/config.js";
vi.mock("../../extensions/browser/src/config/config.js", async (importOriginal) => {
const actual =
@@ -21,12 +27,8 @@ vi.mock("../../extensions/browser/src/browser/chrome.js", () => ({
resolveOpenClawUserDataDir: vi.fn(() => "/tmp/openclaw-test/openclaw/user-data"),
}));
import { resolveOpenClawUserDataDir } from "../../extensions/browser/src/browser/chrome.js";
import { movePathToTrash } from "../../extensions/browser/src/browser/trash.js";
import { loadConfig, writeConfigFile } from "../../extensions/browser/src/config/config.js";
let resolveBrowserConfig: typeof import("./config.js").resolveBrowserConfig;
let createBrowserProfilesService: typeof import("./profiles-service.js").createBrowserProfilesService;
let resolveBrowserConfig: typeof import("../../extensions/browser/src/browser/config.js").resolveBrowserConfig;
let createBrowserProfilesService: typeof import("../../extensions/browser/src/browser/profiles-service.js").createBrowserProfilesService;
function createCtx(resolved: BrowserServerState["resolved"]) {
const state: BrowserServerState = {
@@ -61,8 +63,9 @@ async function createWorkProfileWithConfig(params: {
describe("BrowserProfilesService", () => {
beforeAll(async () => {
vi.resetModules();
({ resolveBrowserConfig } = await import("./config.js"));
({ createBrowserProfilesService } = await import("./profiles-service.js"));
({ resolveBrowserConfig } = await import("../../extensions/browser/src/browser/config.js"));
({ createBrowserProfilesService } =
await import("../../extensions/browser/src/browser/profiles-service.js"));
});
beforeEach(() => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/profiles-service.js";

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { resolveBrowserConfig } from "./config.js";
import { resolveBrowserConfig } from "../../extensions/browser/src/browser/config.js";
import {
allocateCdpPort,
allocateColor,
@@ -9,7 +9,7 @@ import {
getUsedPorts,
isValidProfileName,
PROFILE_COLORS,
} from "./profiles.js";
} from "../../extensions/browser/src/browser/profiles.js";
describe("profile name validation", () => {
it.each(["openclaw", "work", "my-profile", "test123", "a", "a-b-c-1-2-3", "1test"])(

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/profiles.js";

View File

@@ -1,9 +1,9 @@
import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { persistBrowserProxyFiles } from "../../extensions/browser/src/browser/proxy-files.js";
import { MEDIA_MAX_BYTES } from "../media/store.js";
import { createTempHomeEnv, type TempHomeEnv } from "../test-utils/temp-home.js";
import { persistBrowserProxyFiles } from "./proxy-files.js";
describe("persistBrowserProxyFiles", () => {
let tempHome: TempHomeEnv;

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/proxy-files.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-ai-module.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-ai-state.js";

View File

@@ -55,16 +55,19 @@ function createBrowser(pages: unknown[]) {
}
let chromiumMock: typeof import("playwright-core").chromium;
let snapshotAiViaPlaywright: typeof import("./pw-tools-core.snapshot.js").snapshotAiViaPlaywright;
let clickViaPlaywright: typeof import("./pw-tools-core.interactions.js").clickViaPlaywright;
let closePlaywrightBrowserConnection: typeof import("./pw-session.js").closePlaywrightBrowserConnection;
let snapshotAiViaPlaywright: typeof import("../../extensions/browser/src/browser/pw-tools-core.snapshot.js").snapshotAiViaPlaywright;
let clickViaPlaywright: typeof import("../../extensions/browser/src/browser/pw-tools-core.interactions.js").clickViaPlaywright;
let closePlaywrightBrowserConnection: typeof import("../../extensions/browser/src/browser/pw-session.js").closePlaywrightBrowserConnection;
beforeAll(async () => {
const pw = await import("playwright-core");
chromiumMock = pw.chromium;
({ snapshotAiViaPlaywright } = await import("./pw-tools-core.snapshot.js"));
({ clickViaPlaywright } = await import("./pw-tools-core.interactions.js"));
({ closePlaywrightBrowserConnection } = await import("./pw-session.js"));
({ snapshotAiViaPlaywright } =
await import("../../extensions/browser/src/browser/pw-tools-core.snapshot.js"));
({ clickViaPlaywright } =
await import("../../extensions/browser/src/browser/pw-tools-core.interactions.js"));
({ closePlaywrightBrowserConnection } =
await import("../../extensions/browser/src/browser/pw-session.js"));
});
afterEach(async () => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-ai.js";

View File

@@ -4,7 +4,7 @@ import {
buildRoleSnapshotFromAriaSnapshot,
getRoleSnapshotStats,
parseRoleRef,
} from "./pw-role-snapshot.js";
} from "../../extensions/browser/src/browser/pw-role-snapshot.js";
describe("pw-role-snapshot", () => {
it("adds refs for interactive elements", () => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-role-snapshot.js";

View File

@@ -14,7 +14,7 @@ async function waitFor(
describeLive("browser (live): remote CDP tab persistence", () => {
it("creates, lists, focuses, and closes tabs via Playwright", { timeout: 60_000 }, async () => {
const pw = await import("./pw-ai.js");
const pw = await import("../../extensions/browser/src/browser/pw-ai.js");
await pw.closePlaywrightBrowserConnection().catch(() => {});
const created = await pw.createPageViaPlaywright({ cdpUrl: CDP_URL, url: "about:blank" });

View File

@@ -1,7 +1,10 @@
import { chromium } from "playwright-core";
import { afterEach, describe, expect, it, vi } from "vitest";
import * as chromeModule from "./chrome.js";
import { closePlaywrightBrowserConnection, listPagesViaPlaywright } from "./pw-session.js";
import * as chromeModule from "../../extensions/browser/src/browser/chrome.js";
import {
closePlaywrightBrowserConnection,
listPagesViaPlaywright,
} from "../../extensions/browser/src/browser/pw-session.js";
const connectOverCdpSpy = vi.spyOn(chromium, "connectOverCDP");
const getChromeWebSocketUrlSpy = vi.spyOn(chromeModule, "getChromeWebSocketUrl");

View File

@@ -1,9 +1,12 @@
import { chromium } from "playwright-core";
import { afterEach, describe, expect, it, vi } from "vitest";
import * as chromeModule from "../../extensions/browser/src/browser/chrome.js";
import { InvalidBrowserNavigationUrlError } from "../../extensions/browser/src/browser/navigation-guard.js";
import {
closePlaywrightBrowserConnection,
createPageViaPlaywright,
} from "../../extensions/browser/src/browser/pw-session.js";
import { SsrFBlockedError } from "../infra/net/ssrf.js";
import * as chromeModule from "./chrome.js";
import { InvalidBrowserNavigationUrlError } from "./navigation-guard.js";
import { closePlaywrightBrowserConnection, createPageViaPlaywright } from "./pw-session.js";
const connectOverCdpSpy = vi.spyOn(chromium, "connectOverCDP");
const getChromeWebSocketUrlSpy = vi.spyOn(chromeModule, "getChromeWebSocketUrl");

View File

@@ -1,7 +1,10 @@
import { chromium } from "playwright-core";
import { afterEach, describe, expect, it, vi } from "vitest";
import * as chromeModule from "./chrome.js";
import { closePlaywrightBrowserConnection, getPageForTargetId } from "./pw-session.js";
import * as chromeModule from "../../extensions/browser/src/browser/chrome.js";
import {
closePlaywrightBrowserConnection,
getPageForTargetId,
} from "../../extensions/browser/src/browser/pw-session.js";
const connectOverCdpSpy = vi.spyOn(chromium, "connectOverCDP");
const getChromeWebSocketUrlSpy = vi.spyOn(chromeModule, "getChromeWebSocketUrl");

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-session.mock-setup.js";

View File

@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { withPageScopedCdpClient } from "./pw-session.page-cdp.js";
import { withPageScopedCdpClient } from "../../extensions/browser/src/browser/pw-session.page-cdp.js";
describe("pw-session page-scoped CDP client", () => {
beforeEach(() => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-session.page-cdp.js";

View File

@@ -5,7 +5,7 @@ import {
refLocator,
rememberRoleRefsForTarget,
restoreRoleRefsForTarget,
} from "./pw-session.js";
} from "../../extensions/browser/src/browser/pw-session.js";
function fakePage(): {
page: Page;

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-session.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.activity.js";

View File

@@ -3,15 +3,15 @@ import {
installPwToolsCoreTestHooks,
setPwToolsCoreCurrentPage,
setPwToolsCoreCurrentRefLocator,
} from "./pw-tools-core.test-harness.js";
} from "../../extensions/browser/src/browser/pw-tools-core.test-harness.js";
installPwToolsCoreTestHooks();
let mod: typeof import("./pw-tools-core.js");
let mod: typeof import("../../extensions/browser/src/browser/pw-tools-core.js");
describe("pw-tools-core", () => {
beforeAll(async () => {
vi.resetModules();
mod = await import("./pw-tools-core.js");
mod = await import("../../extensions/browser/src/browser/pw-tools-core.js");
});
it("clamps timeoutMs for scrollIntoView", async () => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.downloads.js";

View File

@@ -18,7 +18,7 @@ const restoreRoleRefsForTarget = vi.fn(() => {});
const closePageViaPlaywright = vi.fn(async () => {});
const resizeViewportViaPlaywright = vi.fn(async () => {});
vi.mock("./pw-session.js", () => ({
vi.mock("../../extensions/browser/src/browser/pw-session.js", () => ({
ensurePageState,
forceDisconnectPlaywrightForTarget,
getPageForTargetId,
@@ -26,17 +26,18 @@ vi.mock("./pw-session.js", () => ({
restoreRoleRefsForTarget,
}));
vi.mock("./pw-tools-core.snapshot.js", () => ({
vi.mock("../../extensions/browser/src/browser/pw-tools-core.snapshot.js", () => ({
closePageViaPlaywright,
resizeViewportViaPlaywright,
}));
let batchViaPlaywright: typeof import("./pw-tools-core.interactions.js").batchViaPlaywright;
let batchViaPlaywright: typeof import("../../extensions/browser/src/browser/pw-tools-core.interactions.js").batchViaPlaywright;
describe("batchViaPlaywright", () => {
beforeAll(async () => {
vi.resetModules();
({ batchViaPlaywright } = await import("./pw-tools-core.interactions.js"));
({ batchViaPlaywright } =
await import("../../extensions/browser/src/browser/pw-tools-core.interactions.js"));
});
beforeEach(() => {

View File

@@ -19,7 +19,7 @@ const refLocator = vi.fn(() => {
return locator;
});
vi.mock("./pw-session.js", () => {
vi.mock("../../extensions/browser/src/browser/pw-session.js", () => {
return {
ensurePageState,
forceDisconnectPlaywrightForTarget,
@@ -29,7 +29,7 @@ vi.mock("./pw-session.js", () => {
};
});
let evaluateViaPlaywright: typeof import("./pw-tools-core.interactions.js").evaluateViaPlaywright;
let evaluateViaPlaywright: typeof import("../../extensions/browser/src/browser/pw-tools-core.interactions.js").evaluateViaPlaywright;
function createPendingEval() {
let evalCalled!: () => void;
@@ -48,7 +48,8 @@ describe("evaluateViaPlaywright (abort)", () => {
vi.clearAllMocks();
page = null;
locator = null;
({ evaluateViaPlaywright } = await import("./pw-tools-core.interactions.js"));
({ evaluateViaPlaywright } =
await import("../../extensions/browser/src/browser/pw-tools-core.interactions.js"));
});
it.each([

View File

@@ -20,9 +20,11 @@ const refLocator = vi.fn(() => {
const forceDisconnectPlaywrightForTarget = vi.fn(async () => {});
const resolveStrictExistingPathsWithinRoot =
vi.fn<typeof import("./paths.js").resolveStrictExistingPathsWithinRoot>();
vi.fn<
typeof import("../../extensions/browser/src/browser/paths.js").resolveStrictExistingPathsWithinRoot
>();
vi.mock("./pw-session.js", () => {
vi.mock("../../extensions/browser/src/browser/pw-session.js", () => {
return {
ensurePageState,
forceDisconnectPlaywrightForTarget,
@@ -32,14 +34,14 @@ vi.mock("./pw-session.js", () => {
};
});
vi.mock("./paths.js", () => {
vi.mock("../../extensions/browser/src/browser/paths.js", () => {
return {
DEFAULT_UPLOAD_DIR: "/tmp/openclaw/uploads",
resolveStrictExistingPathsWithinRoot,
};
});
let setInputFilesViaPlaywright: typeof import("./pw-tools-core.interactions.js").setInputFilesViaPlaywright;
let setInputFilesViaPlaywright: typeof import("../../extensions/browser/src/browser/pw-tools-core.interactions.js").setInputFilesViaPlaywright;
function seedSingleLocatorPage(): { setInputFiles: ReturnType<typeof vi.fn> } {
const setInputFiles = vi.fn(async () => {});
@@ -56,7 +58,8 @@ function seedSingleLocatorPage(): { setInputFiles: ReturnType<typeof vi.fn> } {
describe("setInputFilesViaPlaywright", () => {
beforeEach(async () => {
vi.resetModules();
({ setInputFilesViaPlaywright } = await import("./pw-tools-core.interactions.js"));
({ setInputFilesViaPlaywright } =
await import("../../extensions/browser/src/browser/pw-tools-core.interactions.js"));
vi.clearAllMocks();
page = null;
locator = null;

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.interactions.js";

View File

@@ -2,19 +2,19 @@ import crypto from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";
import { beforeAll, describe, expect, it, vi } from "vitest";
import { DEFAULT_UPLOAD_DIR } from "./paths.js";
import { DEFAULT_UPLOAD_DIR } from "../../extensions/browser/src/browser/paths.js";
import {
installPwToolsCoreTestHooks,
setPwToolsCoreCurrentPage,
} from "./pw-tools-core.test-harness.js";
} from "../../extensions/browser/src/browser/pw-tools-core.test-harness.js";
installPwToolsCoreTestHooks();
let mod: typeof import("./pw-tools-core.js");
let mod: typeof import("../../extensions/browser/src/browser/pw-tools-core.js");
describe("pw-tools-core", () => {
beforeAll(async () => {
vi.resetModules();
mod = await import("./pw-tools-core.js");
mod = await import("../../extensions/browser/src/browser/pw-tools-core.js");
});
it("last file-chooser arm wins", async () => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.responses.js";

View File

@@ -2,17 +2,17 @@ import crypto from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { DEFAULT_UPLOAD_DIR } from "./paths.js";
import { DEFAULT_UPLOAD_DIR } from "../../extensions/browser/src/browser/paths.js";
import {
getPwToolsCoreSessionMocks,
installPwToolsCoreTestHooks,
setPwToolsCoreCurrentPage,
setPwToolsCoreCurrentRefLocator,
} from "./pw-tools-core.test-harness.js";
} from "../../extensions/browser/src/browser/pw-tools-core.test-harness.js";
installPwToolsCoreTestHooks();
const sessionMocks = getPwToolsCoreSessionMocks();
let mod: typeof import("./pw-tools-core.js");
let mod: typeof import("../../extensions/browser/src/browser/pw-tools-core.js");
function createFileChooserPageMocks() {
const fileChooser = { setFiles: vi.fn(async () => {}) };
@@ -28,7 +28,7 @@ function createFileChooserPageMocks() {
describe("pw-tools-core", () => {
beforeAll(async () => {
vi.resetModules();
mod = await import("./pw-tools-core.js");
mod = await import("../../extensions/browser/src/browser/pw-tools-core.js");
});
beforeEach(() => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.shared.js";

View File

@@ -1,14 +1,14 @@
import { describe, expect, it, vi } from "vitest";
import { SsrFBlockedError } from "../infra/net/ssrf.js";
import { InvalidBrowserNavigationUrlError } from "./navigation-guard.js";
import { InvalidBrowserNavigationUrlError } from "../../extensions/browser/src/browser/navigation-guard.js";
import {
getPwToolsCoreSessionMocks,
installPwToolsCoreTestHooks,
setPwToolsCoreCurrentPage,
} from "./pw-tools-core.test-harness.js";
} from "../../extensions/browser/src/browser/pw-tools-core.test-harness.js";
import { SsrFBlockedError } from "../infra/net/ssrf.js";
installPwToolsCoreTestHooks();
const mod = await import("./pw-tools-core.snapshot.js");
const mod = await import("../../extensions/browser/src/browser/pw-tools-core.snapshot.js");
describe("pw-tools-core.snapshot navigate guard", () => {
it("blocks unsupported non-network URLs before page lookup", async () => {

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.snapshot.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.state.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.storage.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.test-harness.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.trace.js";

View File

@@ -1 +0,0 @@
export * from "../../extensions/browser/src/browser/pw-tools-core.js";

View File

@@ -7,7 +7,7 @@ import {
installPwToolsCoreTestHooks,
setPwToolsCoreCurrentPage,
setPwToolsCoreCurrentRefLocator,
} from "./pw-tools-core.test-harness.js";
} from "../../extensions/browser/src/browser/pw-tools-core.test-harness.js";
installPwToolsCoreTestHooks();
const sessionMocks = getPwToolsCoreSessionMocks();
@@ -15,12 +15,12 @@ const tmpDirMocks = vi.hoisted(() => ({
resolvePreferredOpenClawTmpDir: vi.fn(() => "/tmp/openclaw"),
}));
vi.mock("../infra/tmp-openclaw-dir.js", () => tmpDirMocks);
let mod: typeof import("./pw-tools-core.js");
let mod: typeof import("../../extensions/browser/src/browser/pw-tools-core.js");
describe("pw-tools-core", () => {
beforeAll(async () => {
vi.resetModules();
mod = await import("./pw-tools-core.js");
mod = await import("../../extensions/browser/src/browser/pw-tools-core.js");
});
beforeEach(() => {

Some files were not shown because too many files have changed in this diff Show More