fix(cycles): remove browser cli and tlon runtime seams

This commit is contained in:
Vincent Koc
2026-04-10 11:43:18 +01:00
parent dbe2a97e80
commit 0e54440ecc
33 changed files with 848 additions and 756 deletions

View File

@@ -1,6 +1,6 @@
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { normalizeString } from "../record-shared.js";
import type { SnapshotAriaNode } from "./client.js";
import type { SnapshotAriaNode } from "./client.types.js";
import {
getRoleSnapshotStats,
type RoleRefMap,

View File

@@ -7,7 +7,7 @@ import { normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/te
import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
import { asRecord } from "../record-shared.js";
import type { ChromeMcpSnapshotNode } from "./chrome-mcp.snapshot.js";
import type { BrowserTab } from "./client.js";
import type { BrowserTab } from "./client.types.js";
import { BrowserProfileUnavailableError, BrowserTabNotFoundError } from "./errors.js";
type ChromeMcpStructuredPage = {

View File

@@ -4,95 +4,10 @@ import type {
BrowserActionTabResult,
} from "./client-actions-types.js";
import { buildProfileQuery, withBaseUrl } from "./client-actions-url.js";
import type { BrowserActRequest, BrowserFormField } from "./client-actions.types.js";
import { fetchBrowserJson } from "./client-fetch.js";
export type BrowserFormField = {
ref: string;
type: string;
value?: string | number | boolean;
};
export type BrowserActRequest =
| {
kind: "click";
ref?: string;
selector?: string;
targetId?: string;
doubleClick?: boolean;
button?: string;
modifiers?: string[];
delayMs?: number;
timeoutMs?: number;
}
| {
kind: "type";
ref?: string;
selector?: string;
text: string;
targetId?: string;
submit?: boolean;
slowly?: boolean;
timeoutMs?: number;
}
| { kind: "press"; key: string; targetId?: string; delayMs?: number }
| {
kind: "hover";
ref?: string;
selector?: string;
targetId?: string;
timeoutMs?: number;
}
| {
kind: "scrollIntoView";
ref?: string;
selector?: string;
targetId?: string;
timeoutMs?: number;
}
| {
kind: "drag";
startRef?: string;
startSelector?: string;
endRef?: string;
endSelector?: string;
targetId?: string;
timeoutMs?: number;
}
| {
kind: "select";
ref?: string;
selector?: string;
values: string[];
targetId?: string;
timeoutMs?: number;
}
| {
kind: "fill";
fields: BrowserFormField[];
targetId?: string;
timeoutMs?: number;
}
| { kind: "resize"; width: number; height: number; targetId?: string }
| {
kind: "wait";
timeMs?: number;
text?: string;
textGone?: string;
selector?: string;
url?: string;
loadState?: "load" | "domcontentloaded" | "networkidle";
fn?: string;
targetId?: string;
timeoutMs?: number;
}
| { kind: "evaluate"; fn: string; ref?: string; targetId?: string; timeoutMs?: number }
| { kind: "close"; targetId?: string }
| {
kind: "batch";
actions: BrowserActRequest[];
targetId?: string;
stopOnError?: boolean;
};
export type { BrowserActRequest, BrowserFormField } from "./client-actions.types.js";
export type BrowserActResponse = {
ok: true;

View File

@@ -0,0 +1,87 @@
export type BrowserFormField = {
ref: string;
type: string;
value?: string | number | boolean;
};
export type BrowserActRequest =
| {
kind: "click";
ref?: string;
selector?: string;
targetId?: string;
doubleClick?: boolean;
button?: string;
modifiers?: string[];
delayMs?: number;
timeoutMs?: number;
}
| {
kind: "type";
ref?: string;
selector?: string;
text: string;
targetId?: string;
submit?: boolean;
slowly?: boolean;
timeoutMs?: number;
}
| { kind: "press"; key: string; targetId?: string; delayMs?: number }
| {
kind: "hover";
ref?: string;
selector?: string;
targetId?: string;
timeoutMs?: number;
}
| {
kind: "scrollIntoView";
ref?: string;
selector?: string;
targetId?: string;
timeoutMs?: number;
}
| {
kind: "drag";
startRef?: string;
startSelector?: string;
endRef?: string;
endSelector?: string;
targetId?: string;
timeoutMs?: number;
}
| {
kind: "select";
ref?: string;
selector?: string;
values: string[];
targetId?: string;
timeoutMs?: number;
}
| {
kind: "fill";
fields: BrowserFormField[];
targetId?: string;
timeoutMs?: number;
}
| { kind: "resize"; width: number; height: number; targetId?: string }
| {
kind: "wait";
timeMs?: number;
text?: string;
textGone?: string;
selector?: string;
url?: string;
loadState?: "load" | "domcontentloaded" | "networkidle";
fn?: string;
targetId?: string;
timeoutMs?: number;
}
| { kind: "evaluate"; fn: string; ref?: string; targetId?: string; timeoutMs?: number }
| { kind: "close"; targetId?: string }
| {
kind: "batch";
actions: BrowserActRequest[];
targetId?: string;
stopOnError?: boolean;
};

View File

@@ -5,12 +5,7 @@ import { loadConfig } from "../config/config.js";
import { isLoopbackHost } from "../gateway/net.js";
import { getBridgeAuthForPort } from "./bridge-auth-registry.js";
import { resolveBrowserControlAuth } from "./control-auth.js";
import {
createBrowserControlContext,
startBrowserControlServiceFromConfig,
} from "./control-service.js";
import { resolveBrowserRateLimitMessage } from "./rate-limit-message.js";
import { createBrowserRouteDispatcher } from "./routes/dispatcher.js";
// Application-level error from the browser control service (service is reachable
// but returned an error response). Must NOT be wrapped with "Can't reach ..." messaging.
@@ -222,11 +217,7 @@ export async function fetchBrowserJson<T>(
return await fetchHttpJson<T>(url, { ...httpInit, timeoutMs });
}
isDispatcherPath = true;
const started = await startBrowserControlServiceFromConfig();
if (!started) {
throw new Error("browser control disabled");
}
const dispatcher = createBrowserRouteDispatcher(createBrowserControlContext());
const { dispatchBrowserControlRequest } = await import("./local-dispatch.runtime.js");
const parsed = new URL(url, "http://localhost");
const query: Record<string, unknown> = {};
for (const [key, value] of parsed.searchParams.entries()) {
@@ -266,7 +257,7 @@ export async function fetchBrowserJson<T>(
timer = setTimeout(() => abortCtrl.abort(new Error("timed out")), timeoutMs);
}
const dispatchPromise = dispatcher.dispatch({
const dispatchPromise = dispatchBrowserControlRequest({
method:
init?.method?.toUpperCase() === "DELETE"
? "DELETE"

View File

@@ -1,6 +1,7 @@
import { fetchBrowserJson } from "./client-fetch.js";
import type { BrowserTab, BrowserTransport, SnapshotAriaNode } from "./client.types.js";
export type BrowserTransport = "cdp" | "chrome-mcp";
export type { BrowserTab, BrowserTransport, SnapshotAriaNode } from "./client.types.js";
export type BrowserStatus = {
enabled: boolean;
@@ -47,24 +48,6 @@ export type BrowserResetProfileResult = {
to?: string;
};
export type BrowserTab = {
targetId: string;
title: string;
url: string;
wsUrl?: string;
type?: string;
};
export type SnapshotAriaNode = {
ref: string;
role: string;
name: string;
value?: string;
description?: string;
backendDOMNodeId?: number;
depth: number;
};
export type SnapshotResult =
| {
ok: true;

View File

@@ -0,0 +1,19 @@
export type BrowserTransport = "cdp" | "chrome-mcp";
export type BrowserTab = {
targetId: string;
title: string;
url: string;
wsUrl?: string;
type?: string;
};
export type SnapshotAriaNode = {
ref: string;
role: string;
name: string;
value?: string;
description?: string;
backendDOMNodeId?: number;
depth: number;
};

View File

@@ -1,5 +1,5 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { BrowserFormField } from "./client-actions-core.js";
import type { BrowserFormField } from "./client-actions.types.js";
export const DEFAULT_FILL_FIELD_TYPE = "text";

View File

@@ -0,0 +1,20 @@
import {
createBrowserControlContext,
startBrowserControlServiceFromConfig,
} from "./control-service.js";
import {
createBrowserRouteDispatcher,
type BrowserDispatchRequest,
type BrowserDispatchResponse,
} from "./routes/dispatcher.js";
export async function dispatchBrowserControlRequest(
req: BrowserDispatchRequest,
): Promise<BrowserDispatchResponse> {
const started = await startBrowserControlServiceFromConfig();
if (!started) {
throw new Error("browser control disabled");
}
const dispatcher = createBrowserRouteDispatcher(createBrowserControlContext());
return await dispatcher.dispatch(req);
}

View File

@@ -10,7 +10,7 @@ import {
resolveActInteractionTimeoutMs,
resolveActWaitTimeoutMs,
} from "./act-policy.js";
import type { BrowserActRequest, BrowserFormField } from "./client-actions-core.js";
import type { BrowserActRequest, BrowserFormField } from "./client-actions.types.js";
import { DEFAULT_FILL_FIELD_TYPE } from "./form-fields.js";
import { DEFAULT_UPLOAD_DIR, resolveStrictExistingPathsWithinRoot } from "./paths.js";
import {

View File

@@ -4,7 +4,7 @@ import {
ACT_MAX_WAIT_TIME_MS,
normalizeActBoundedNonNegativeMs,
} from "../act-policy.js";
import type { BrowserActRequest, BrowserFormField } from "../client-actions-core.js";
import type { BrowserActRequest, BrowserFormField } from "../client-actions.types.js";
import { normalizeBrowserFormField } from "../form-fields.js";
import {
type ActKind,

View File

@@ -10,7 +10,7 @@ import {
pressChromeMcpKey,
resizeChromeMcpPage,
} from "../chrome-mcp.js";
import type { BrowserActRequest } from "../client-actions-core.js";
import type { BrowserActRequest } from "../client-actions.types.js";
import { getBrowserProfileCapabilities } from "../profile-capabilities.js";
import type { BrowserRouteContext } from "../server-context.js";
import { matchBrowserUrlPattern } from "../url-pattern.js";

View File

@@ -1,7 +1,6 @@
import type { Server } from "node:http";
import type { RunningChrome } from "./chrome.js";
import type { BrowserTransport } from "./client.js";
import type { BrowserTab } from "./client.js";
import type { BrowserTab, BrowserTransport } from "./client.types.js";
import type { ResolvedBrowserConfig, ResolvedBrowserProfile } from "./config.js";
export type { BrowserTab };