feat(cli): expand browser commands

This commit is contained in:
Peter Steinberger
2026-01-12 17:31:59 +00:00
parent cf78d28d74
commit 35bbc2ba87
6 changed files with 851 additions and 3 deletions

View File

@@ -454,16 +454,32 @@ export function registerBrowserActionInputCommands(
browser
.command("wait")
.description("Wait for time or text conditions")
.description("Wait for time, selector, URL, load state, or JS conditions")
.argument("[selector]", "CSS selector to wait for (visible)")
.option("--time <ms>", "Wait for N milliseconds", (v: string) => Number(v))
.option("--text <value>", "Wait for text to appear")
.option("--text-gone <value>", "Wait for text to disappear")
.option("--url <pattern>", "Wait for URL (supports globs like **/dash)")
.option("--load <load|domcontentloaded|networkidle>", "Wait for load state")
.option("--fn <js>", "Wait for JS condition (passed to waitForFunction)")
.option(
"--timeout-ms <ms>",
"How long to wait for each condition (default: 20000)",
(v: string) => Number(v),
)
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (opts, cmd) => {
.action(async (selector: string | undefined, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const sel = selector?.trim() || undefined;
const load =
opts.load === "load" ||
opts.load === "domcontentloaded" ||
opts.load === "networkidle"
? (opts.load as "load" | "domcontentloaded" | "networkidle")
: undefined;
const result = await browserAct(
baseUrl,
{
@@ -471,7 +487,14 @@ export function registerBrowserActionInputCommands(
timeMs: Number.isFinite(opts.time) ? opts.time : undefined,
text: opts.text?.trim() || undefined,
textGone: opts.textGone?.trim() || undefined,
selector: sel,
url: opts.url?.trim() || undefined,
loadState: load,
fn: opts.fn?.trim() || undefined,
targetId: opts.targetId?.trim() || undefined,
timeoutMs: Number.isFinite(opts.timeoutMs)
? opts.timeoutMs
: undefined,
},
{ profile },
);

View File

@@ -0,0 +1,182 @@
import type { Command } from "commander";
import { resolveBrowserControlUrl } from "../browser/client.js";
import {
browserHighlight,
browserPageErrors,
browserRequests,
browserTraceStart,
browserTraceStop,
} from "../browser/client-actions.js";
import { danger } from "../globals.js";
import { defaultRuntime } from "../runtime.js";
import type { BrowserParentOpts } from "./browser-cli-shared.js";
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 baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserHighlight(baseUrl, {
ref: ref.trim(),
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`highlighted ${ref.trim()}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
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 baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserPageErrors(baseUrl, {
targetId: opts.targetId?.trim() || undefined,
clear: Boolean(opts.clear),
profile,
});
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"),
);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
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 baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserRequests(baseUrl, {
targetId: opts.targetId?.trim() || undefined,
filter: opts.filter?.trim() || undefined,
clear: Boolean(opts.clear),
profile,
});
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"),
);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
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 baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserTraceStart(baseUrl, {
targetId: opts.targetId?.trim() || undefined,
screenshots: Boolean(opts.screenshots),
snapshots: Boolean(opts.snapshots),
sources: Boolean(opts.sources),
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log("trace started");
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
trace
.command("stop")
.description("Stop trace recording and write a .zip")
.option("--out <path>", "Output path for the trace zip")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserTraceStop(baseUrl, {
targetId: opts.targetId?.trim() || undefined,
path: opts.out?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`TRACE:${result.path}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
}

View File

@@ -59,6 +59,7 @@ export function registerBrowserInspectCommands(
.option("--compact", "Role snapshot: compact output", false)
.option("--depth <n>", "Role snapshot: max depth", (v: string) => Number(v))
.option("--selector <sel>", "Role snapshot: scope to CSS selector")
.option("--frame <sel>", "Role snapshot: scope to an iframe selector")
.option("--out <path>", "Write snapshot to a file")
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
@@ -74,6 +75,7 @@ export function registerBrowserInspectCommands(
compact: Boolean(opts.compact) || undefined,
depth: Number.isFinite(opts.depth) ? opts.depth : undefined,
selector: opts.selector?.trim() || undefined,
frame: opts.frame?.trim() || undefined,
profile,
});

View File

@@ -1,5 +1,5 @@
import type { Command } from "commander";
import type { BrowserTab } from "../browser/client.js";
import {
browserCloseTab,
browserCreateProfile,
@@ -11,6 +11,7 @@ import {
browserStart,
browserStatus,
browserStop,
browserTabAction,
browserTabs,
resolveBrowserControlUrl,
} from "../browser/client.js";
@@ -159,6 +160,128 @@ export function registerBrowserManageCommands(
}
});
const tab = browser
.command("tab")
.description("Tab shortcuts (index-based)")
.action(async (_opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = (await browserTabAction(baseUrl, {
action: "list",
profile,
})) as { ok: true; tabs: BrowserTab[] };
const tabs = result.tabs ?? [];
if (parent?.json) {
defaultRuntime.log(JSON.stringify({ tabs }, null, 2));
return;
}
if (tabs.length === 0) {
defaultRuntime.log("No tabs (browser closed or no targets).");
return;
}
defaultRuntime.log(
tabs
.map(
(t, i) =>
`${i + 1}. ${t.title || "(untitled)"}\n ${t.url}\n id: ${t.targetId}`,
)
.join("\n"),
);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
tab
.command("new")
.description("Open a new tab (about:blank)")
.action(async (_opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserTabAction(baseUrl, {
action: "new",
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log("opened new tab");
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
tab
.command("select")
.description("Focus tab by index (1-based)")
.argument("<index>", "Tab index (1-based)", (v: string) => Number(v))
.action(async (index: number, _opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
if (!Number.isFinite(index) || index < 1) {
defaultRuntime.error(danger("index must be a positive number"));
defaultRuntime.exit(1);
return;
}
try {
const result = await browserTabAction(baseUrl, {
action: "select",
index: Math.floor(index) - 1,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`selected tab ${Math.floor(index)}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
tab
.command("close")
.description("Close tab by index (1-based); default: first tab")
.argument("[index]", "Tab index (1-based)", (v: string) => Number(v))
.action(async (index: number | undefined, _opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
const idx =
typeof index === "number" && Number.isFinite(index)
? Math.floor(index) - 1
: undefined;
if (typeof idx === "number" && idx < 0) {
defaultRuntime.error(danger("index must be >= 1"));
defaultRuntime.exit(1);
return;
}
try {
const result = await browserTabAction(baseUrl, {
action: "close",
index: idx,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log("closed tab");
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
browser
.command("open")
.description("Open a URL in a new tab")

View File

@@ -0,0 +1,514 @@
import type { Command } from "commander";
import { resolveBrowserControlUrl } from "../browser/client.js";
import {
browserCookies,
browserCookiesClear,
browserCookiesSet,
browserSetDevice,
browserSetGeolocation,
browserSetHeaders,
browserSetHttpCredentials,
browserSetLocale,
browserSetMedia,
browserSetOffline,
browserSetTimezone,
browserStorageClear,
browserStorageGet,
browserStorageSet,
} from "../browser/client-actions.js";
import { browserAct } from "../browser/client-actions-core.js";
import { danger } from "../globals.js";
import { defaultRuntime } from "../runtime.js";
import type { BrowserParentOpts } from "./browser-cli-shared.js";
function parseOnOff(raw: string): boolean | null {
const v = raw.trim().toLowerCase();
if (v === "on" || v === "true" || v === "1") return true;
if (v === "off" || v === "false" || v === "0") return false;
return null;
}
export function registerBrowserStateCommands(
browser: Command,
parentOpts: (cmd: Command) => BrowserParentOpts,
) {
const cookies = browser.command("cookies").description("Read/write cookies");
cookies
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserCookies(baseUrl, {
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(JSON.stringify(result.cookies ?? [], null, 2));
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
cookies
.command("set")
.description("Set a cookie (requires --url or domain+path)")
.argument("<name>", "Cookie name")
.argument("<value>", "Cookie value")
.requiredOption("--url <url>", "Cookie URL scope (recommended)")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (name: string, value: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserCookiesSet(baseUrl, {
targetId: opts.targetId?.trim() || undefined,
cookie: { name, value, url: opts.url },
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`cookie set: ${name}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
cookies
.command("clear")
.description("Clear all cookies")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserCookiesClear(baseUrl, {
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log("cookies cleared");
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
const storage = browser
.command("storage")
.description("Read/write localStorage/sessionStorage");
function registerStorageKind(kind: "local" | "session") {
const cmd = storage.command(kind).description(`${kind}Storage commands`);
cmd
.command("get")
.description(`Get ${kind}Storage (all keys or one key)`)
.argument("[key]", "Key (optional)")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (key: string | undefined, opts, cmd2) => {
const parent = parentOpts(cmd2);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserStorageGet(baseUrl, {
kind,
key: key?.trim() || undefined,
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(JSON.stringify(result.values ?? {}, null, 2));
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
cmd
.command("set")
.description(`Set a ${kind}Storage key`)
.argument("<key>", "Key")
.argument("<value>", "Value")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (key: string, value: string, opts, cmd2) => {
const parent = parentOpts(cmd2);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserStorageSet(baseUrl, {
kind,
key,
value,
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`${kind}Storage set: ${key}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
cmd
.command("clear")
.description(`Clear all ${kind}Storage keys`)
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (opts, cmd2) => {
const parent = parentOpts(cmd2);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserStorageClear(baseUrl, {
kind,
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`${kind}Storage cleared`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
}
registerStorageKind("local");
registerStorageKind("session");
const set = browser
.command("set")
.description("Browser environment settings");
set
.command("viewport")
.description("Set viewport size (alias for resize)")
.argument("<width>", "Viewport width", (v: string) => Number(v))
.argument("<height>", "Viewport height", (v: string) => Number(v))
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (width: number, height: number, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
if (!Number.isFinite(width) || !Number.isFinite(height)) {
defaultRuntime.error(danger("width and height must be numbers"));
defaultRuntime.exit(1);
return;
}
try {
const result = await browserAct(
baseUrl,
{
kind: "resize",
width,
height,
targetId: opts.targetId?.trim() || undefined,
},
{ profile },
);
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`viewport set: ${width}x${height}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
set
.command("offline")
.description("Toggle offline mode")
.argument("<on|off>", "on/off")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (value: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
const offline = parseOnOff(value);
if (offline === null) {
defaultRuntime.error(danger("Expected on|off"));
defaultRuntime.exit(1);
return;
}
try {
const result = await browserSetOffline(baseUrl, {
offline,
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`offline: ${offline}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
set
.command("headers")
.description("Set extra HTTP headers (JSON object)")
.requiredOption("--json <json>", "JSON object of headers")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const parsed = JSON.parse(String(opts.json)) as unknown;
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error("headers json must be an object");
}
const headers: Record<string, string> = {};
for (const [k, v] of Object.entries(
parsed as Record<string, unknown>,
)) {
if (typeof v === "string") headers[k] = v;
}
const result = await browserSetHeaders(baseUrl, {
headers,
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log("headers set");
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
set
.command("credentials")
.description("Set HTTP basic auth credentials")
.option("--clear", "Clear credentials", false)
.argument("[username]", "Username")
.argument("[password]", "Password")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(
async (
username: string | undefined,
password: string | undefined,
opts,
cmd,
) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserSetHttpCredentials(baseUrl, {
username: username?.trim() || undefined,
password,
clear: Boolean(opts.clear),
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(
opts.clear ? "credentials cleared" : "credentials set",
);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
},
);
set
.command("geo")
.description("Set geolocation (and grant permission)")
.option("--clear", "Clear geolocation + permissions", false)
.argument("[latitude]", "Latitude", (v: string) => Number(v))
.argument("[longitude]", "Longitude", (v: string) => Number(v))
.option("--accuracy <m>", "Accuracy in meters", (v: string) => Number(v))
.option("--origin <origin>", "Origin to grant permissions for")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(
async (
latitude: number | undefined,
longitude: number | undefined,
opts,
cmd,
) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserSetGeolocation(baseUrl, {
latitude: Number.isFinite(latitude) ? latitude : undefined,
longitude: Number.isFinite(longitude) ? longitude : undefined,
accuracy: Number.isFinite(opts.accuracy)
? opts.accuracy
: undefined,
origin: opts.origin?.trim() || undefined,
clear: Boolean(opts.clear),
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(
opts.clear ? "geolocation cleared" : "geolocation set",
);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
},
);
set
.command("media")
.description("Emulate prefers-color-scheme")
.argument("<dark|light|none>", "dark/light/none")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (value: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
const v = value.trim().toLowerCase();
const colorScheme =
v === "dark"
? "dark"
: v === "light"
? "light"
: v === "none"
? "none"
: null;
if (!colorScheme) {
defaultRuntime.error(danger("Expected dark|light|none"));
defaultRuntime.exit(1);
return;
}
try {
const result = await browserSetMedia(baseUrl, {
colorScheme,
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`media colorScheme: ${colorScheme}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
set
.command("timezone")
.description("Override timezone (CDP)")
.argument("<timezoneId>", "Timezone ID (e.g. America/New_York)")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (timezoneId: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserSetTimezone(baseUrl, {
timezoneId,
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`timezone: ${timezoneId}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
set
.command("locale")
.description("Override locale (CDP)")
.argument("<locale>", "Locale (e.g. en-US)")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (locale: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserSetLocale(baseUrl, {
locale,
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`locale: ${locale}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
set
.command("device")
.description('Apply a Playwright device descriptor (e.g. "iPhone 14")')
.argument("<name>", "Device name (Playwright devices)")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (name: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.browserProfile;
try {
const result = await browserSetDevice(baseUrl, {
name,
targetId: opts.targetId?.trim() || undefined,
profile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
}
defaultRuntime.log(`device: ${name}`);
} catch (err) {
defaultRuntime.error(danger(String(err)));
defaultRuntime.exit(1);
}
});
}

View File

@@ -6,6 +6,7 @@ import { formatDocsLink } from "../terminal/links.js";
import { theme } from "../terminal/theme.js";
import { registerBrowserActionInputCommands } from "./browser-cli-actions-input.js";
import { registerBrowserActionObserveCommands } from "./browser-cli-actions-observe.js";
import { registerBrowserDebugCommands } from "./browser-cli-debug.js";
import {
browserActionExamples,
browserCoreExamples,
@@ -13,6 +14,7 @@ import {
import { registerBrowserInspectCommands } from "./browser-cli-inspect.js";
import { registerBrowserManageCommands } from "./browser-cli-manage.js";
import type { BrowserParentOpts } from "./browser-cli-shared.js";
import { registerBrowserStateCommands } from "./browser-cli-state.js";
export function registerBrowserCli(program: Command) {
const browser = program
@@ -50,4 +52,6 @@ export function registerBrowserCli(program: Command) {
registerBrowserInspectCommands(browser, parentOpts);
registerBrowserActionInputCommands(browser, parentOpts);
registerBrowserActionObserveCommands(browser, parentOpts);
registerBrowserDebugCommands(browser, parentOpts);
registerBrowserStateCommands(browser, parentOpts);
}