mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
226 lines
7.1 KiB
TypeScript
226 lines
7.1 KiB
TypeScript
import type { Command } from "commander";
|
|
import { danger } from "../globals.js";
|
|
import { defaultRuntime } from "../runtime.js";
|
|
import { shortenHomePath } from "../utils.js";
|
|
import { callBrowserRequest, type BrowserParentOpts } from "./browser-cli-shared.js";
|
|
import { runCommandWithRuntime } from "./cli-utils.js";
|
|
|
|
function runBrowserDebug(action: () => Promise<void>) {
|
|
return runCommandWithRuntime(defaultRuntime, action, (err) => {
|
|
defaultRuntime.error(danger(String(err)));
|
|
defaultRuntime.exit(1);
|
|
});
|
|
}
|
|
|
|
function resolveDebugQuery(params: {
|
|
targetId?: unknown;
|
|
clear?: unknown;
|
|
profile?: string;
|
|
filter?: unknown;
|
|
}) {
|
|
return {
|
|
targetId: typeof params.targetId === "string" ? params.targetId.trim() || undefined : undefined,
|
|
filter: typeof params.filter === "string" ? params.filter.trim() || undefined : undefined,
|
|
clear: Boolean(params.clear),
|
|
profile: params.profile,
|
|
};
|
|
}
|
|
|
|
export function registerBrowserDebugCommands(
|
|
browser: Command,
|
|
parentOpts: (cmd: Command) => BrowserParentOpts,
|
|
) {
|
|
browser
|
|
.command("highlight")
|
|
.description("Highlight an element by ref")
|
|
.argument("<ref>", "Ref id from snapshot")
|
|
.option("--target-id <id>", "CDP target id (or unique prefix)")
|
|
.action(async (ref: string, opts, cmd) => {
|
|
const parent = parentOpts(cmd);
|
|
const profile = parent?.browserProfile;
|
|
await runBrowserDebug(async () => {
|
|
const result = await callBrowserRequest(
|
|
parent,
|
|
{
|
|
method: "POST",
|
|
path: "/highlight",
|
|
query: profile ? { profile } : undefined,
|
|
body: {
|
|
ref: ref.trim(),
|
|
targetId: opts.targetId?.trim() || undefined,
|
|
},
|
|
},
|
|
{ timeoutMs: 20000 },
|
|
);
|
|
if (parent?.json) {
|
|
defaultRuntime.log(JSON.stringify(result, null, 2));
|
|
return;
|
|
}
|
|
defaultRuntime.log(`highlighted ${ref.trim()}`);
|
|
});
|
|
});
|
|
|
|
browser
|
|
.command("errors")
|
|
.description("Get recent page errors")
|
|
.option("--clear", "Clear stored errors after reading", false)
|
|
.option("--target-id <id>", "CDP target id (or unique prefix)")
|
|
.action(async (opts, cmd) => {
|
|
const parent = parentOpts(cmd);
|
|
const profile = parent?.browserProfile;
|
|
await runBrowserDebug(async () => {
|
|
const result = await callBrowserRequest<{
|
|
errors: Array<{ timestamp: string; name?: string; message: string }>;
|
|
}>(
|
|
parent,
|
|
{
|
|
method: "GET",
|
|
path: "/errors",
|
|
query: resolveDebugQuery({
|
|
targetId: opts.targetId,
|
|
clear: opts.clear,
|
|
profile,
|
|
}),
|
|
},
|
|
{ timeoutMs: 20000 },
|
|
);
|
|
if (parent?.json) {
|
|
defaultRuntime.log(JSON.stringify(result, null, 2));
|
|
return;
|
|
}
|
|
if (!result.errors.length) {
|
|
defaultRuntime.log("No page errors.");
|
|
return;
|
|
}
|
|
defaultRuntime.log(
|
|
result.errors
|
|
.map((e) => `${e.timestamp} ${e.name ? `${e.name}: ` : ""}${e.message}`)
|
|
.join("\n"),
|
|
);
|
|
});
|
|
});
|
|
|
|
browser
|
|
.command("requests")
|
|
.description("Get recent network requests (best-effort)")
|
|
.option("--filter <text>", "Only show URLs that contain this substring")
|
|
.option("--clear", "Clear stored requests after reading", false)
|
|
.option("--target-id <id>", "CDP target id (or unique prefix)")
|
|
.action(async (opts, cmd) => {
|
|
const parent = parentOpts(cmd);
|
|
const profile = parent?.browserProfile;
|
|
await runBrowserDebug(async () => {
|
|
const result = await callBrowserRequest<{
|
|
requests: Array<{
|
|
timestamp: string;
|
|
method: string;
|
|
status?: number;
|
|
ok?: boolean;
|
|
url: string;
|
|
failureText?: string;
|
|
}>;
|
|
}>(
|
|
parent,
|
|
{
|
|
method: "GET",
|
|
path: "/requests",
|
|
query: resolveDebugQuery({
|
|
targetId: opts.targetId,
|
|
filter: opts.filter,
|
|
clear: opts.clear,
|
|
profile,
|
|
}),
|
|
},
|
|
{ timeoutMs: 20000 },
|
|
);
|
|
if (parent?.json) {
|
|
defaultRuntime.log(JSON.stringify(result, null, 2));
|
|
return;
|
|
}
|
|
if (!result.requests.length) {
|
|
defaultRuntime.log("No requests recorded.");
|
|
return;
|
|
}
|
|
defaultRuntime.log(
|
|
result.requests
|
|
.map((r) => {
|
|
const status = typeof r.status === "number" ? ` ${r.status}` : "";
|
|
const ok = r.ok === true ? " ok" : r.ok === false ? " fail" : "";
|
|
const fail = r.failureText ? ` (${r.failureText})` : "";
|
|
return `${r.timestamp} ${r.method}${status}${ok} ${r.url}${fail}`;
|
|
})
|
|
.join("\n"),
|
|
);
|
|
});
|
|
});
|
|
|
|
const trace = browser.command("trace").description("Record a Playwright trace");
|
|
|
|
trace
|
|
.command("start")
|
|
.description("Start trace recording")
|
|
.option("--target-id <id>", "CDP target id (or unique prefix)")
|
|
.option("--no-screenshots", "Disable screenshots")
|
|
.option("--no-snapshots", "Disable snapshots")
|
|
.option("--sources", "Include sources (bigger traces)", false)
|
|
.action(async (opts, cmd) => {
|
|
const parent = parentOpts(cmd);
|
|
const profile = parent?.browserProfile;
|
|
await runBrowserDebug(async () => {
|
|
const result = await callBrowserRequest(
|
|
parent,
|
|
{
|
|
method: "POST",
|
|
path: "/trace/start",
|
|
query: profile ? { profile } : undefined,
|
|
body: {
|
|
targetId: opts.targetId?.trim() || undefined,
|
|
screenshots: Boolean(opts.screenshots),
|
|
snapshots: Boolean(opts.snapshots),
|
|
sources: Boolean(opts.sources),
|
|
},
|
|
},
|
|
{ timeoutMs: 20000 },
|
|
);
|
|
if (parent?.json) {
|
|
defaultRuntime.log(JSON.stringify(result, null, 2));
|
|
return;
|
|
}
|
|
defaultRuntime.log("trace started");
|
|
});
|
|
});
|
|
|
|
trace
|
|
.command("stop")
|
|
.description("Stop trace recording and write a .zip")
|
|
.option(
|
|
"--out <path>",
|
|
"Output path within openclaw temp dir (e.g. trace.zip or /tmp/openclaw/trace.zip)",
|
|
)
|
|
.option("--target-id <id>", "CDP target id (or unique prefix)")
|
|
.action(async (opts, cmd) => {
|
|
const parent = parentOpts(cmd);
|
|
const profile = parent?.browserProfile;
|
|
await runBrowserDebug(async () => {
|
|
const result = await callBrowserRequest<{ path: string }>(
|
|
parent,
|
|
{
|
|
method: "POST",
|
|
path: "/trace/stop",
|
|
query: profile ? { profile } : undefined,
|
|
body: {
|
|
targetId: opts.targetId?.trim() || undefined,
|
|
path: opts.out?.trim() || undefined,
|
|
},
|
|
},
|
|
{ timeoutMs: 20000 },
|
|
);
|
|
if (parent?.json) {
|
|
defaultRuntime.log(JSON.stringify(result, null, 2));
|
|
return;
|
|
}
|
|
defaultRuntime.log(`TRACE:${shortenHomePath(result.path)}`);
|
|
});
|
|
});
|
|
}
|