fix(browser-cli): rename --profile to --browser-profile to avoid conflict with global --profile flag

This commit is contained in:
James Groat
2026-01-06 09:54:31 -07:00
parent 30ea1e37f2
commit 6cebd26529
9 changed files with 127 additions and 41 deletions

View File

@@ -221,7 +221,7 @@ The agent should not assume tabs are ephemeral. It should:
## CLI quick reference (one example each)
All commands accept `--profile <name>` to target a specific profile (default: `clawd`).
All commands accept `--browser-profile <name>` to target a specific profile (default: `clawd`).
Profile management:
- `clawdbot browser profiles`

View File

@@ -118,6 +118,7 @@ const BrowserToolSchema = Type.Object({
Type.Literal("dialog"),
Type.Literal("act"),
]),
profile: Type.Optional(Type.String()),
controlUrl: Type.Optional(Type.String()),
targetUrl: Type.Optional(Type.String()),
targetId: Type.Optional(Type.String()),
@@ -161,38 +162,39 @@ export function createBrowserTool(opts?: {
const params = args as Record<string, unknown>;
const action = readStringParam(params, "action", { required: true });
const controlUrl = readStringParam(params, "controlUrl");
const profile = readStringParam(params, "profile");
const baseUrl = resolveBrowserBaseUrl(
controlUrl ?? opts?.defaultControlUrl,
);
switch (action) {
case "status":
return jsonResult(await browserStatus(baseUrl));
return jsonResult(await browserStatus(baseUrl, { profile }));
case "start":
await browserStart(baseUrl);
return jsonResult(await browserStatus(baseUrl));
await browserStart(baseUrl, { profile });
return jsonResult(await browserStatus(baseUrl, { profile }));
case "stop":
await browserStop(baseUrl);
return jsonResult(await browserStatus(baseUrl));
await browserStop(baseUrl, { profile });
return jsonResult(await browserStatus(baseUrl, { profile }));
case "tabs":
return jsonResult({ tabs: await browserTabs(baseUrl) });
return jsonResult({ tabs: await browserTabs(baseUrl, { profile }) });
case "open": {
const targetUrl = readStringParam(params, "targetUrl", {
required: true,
});
return jsonResult(await browserOpenTab(baseUrl, targetUrl));
return jsonResult(await browserOpenTab(baseUrl, targetUrl, { profile }));
}
case "focus": {
const targetId = readStringParam(params, "targetId", {
required: true,
});
await browserFocusTab(baseUrl, targetId);
await browserFocusTab(baseUrl, targetId, { profile });
return jsonResult({ ok: true });
}
case "close": {
const targetId = readStringParam(params, "targetId");
if (targetId) await browserCloseTab(baseUrl, targetId);
else await browserAct(baseUrl, { kind: "close" });
if (targetId) await browserCloseTab(baseUrl, targetId, { profile });
else await browserAct(baseUrl, { kind: "close" }, { profile });
return jsonResult({ ok: true });
}
case "snapshot": {
@@ -212,6 +214,7 @@ export function createBrowserTool(opts?: {
format,
targetId,
limit,
profile,
});
if (snapshot.format === "ai") {
return {
@@ -233,6 +236,7 @@ export function createBrowserTool(opts?: {
ref,
element,
type,
profile,
});
return await imageResultFromFile({
label: "browser:screenshot",
@@ -246,7 +250,7 @@ export function createBrowserTool(opts?: {
});
const targetId = readStringParam(params, "targetId");
return jsonResult(
await browserNavigate(baseUrl, { url: targetUrl, targetId }),
await browserNavigate(baseUrl, { url: targetUrl, targetId, profile }),
);
}
case "console": {
@@ -257,7 +261,7 @@ export function createBrowserTool(opts?: {
? params.targetId.trim()
: undefined;
return jsonResult(
await browserConsoleMessages(baseUrl, { level, targetId }),
await browserConsoleMessages(baseUrl, { level, targetId, profile }),
);
}
case "pdf": {
@@ -265,7 +269,7 @@ export function createBrowserTool(opts?: {
typeof params.targetId === "string"
? params.targetId.trim()
: undefined;
const result = await browserPdfSave(baseUrl, { targetId });
const result = await browserPdfSave(baseUrl, { targetId, profile });
return {
content: [{ type: "text", text: `FILE:${result.path}` }],
details: result,
@@ -296,6 +300,7 @@ export function createBrowserTool(opts?: {
element,
targetId,
timeoutMs,
profile,
}),
);
}
@@ -320,6 +325,7 @@ export function createBrowserTool(opts?: {
promptText,
targetId,
timeoutMs,
profile,
}),
);
}
@@ -331,6 +337,7 @@ export function createBrowserTool(opts?: {
const result = await browserAct(
baseUrl,
request as Parameters<typeof browserAct>[1],
{ profile },
);
return jsonResult(result);
}

View File

@@ -64,7 +64,7 @@ export function registerBrowserActionInputCommands(
.action(async (url: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserNavigate(baseUrl, {
url,
@@ -91,7 +91,7 @@ export function registerBrowserActionInputCommands(
.action(async (width: number, height: number, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
if (!Number.isFinite(width) || !Number.isFinite(height)) {
defaultRuntime.error(danger("width and height must be numbers"));
defaultRuntime.exit(1);
@@ -130,7 +130,7 @@ export function registerBrowserActionInputCommands(
.action(async (ref: string | undefined, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
const refValue = typeof ref === "string" ? ref.trim() : "";
if (!refValue) {
defaultRuntime.error(danger("ref is required"));
@@ -179,7 +179,7 @@ export function registerBrowserActionInputCommands(
.action(async (ref: string | undefined, text: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
const refValue = typeof ref === "string" ? ref.trim() : "";
if (!refValue) {
defaultRuntime.error(danger("ref is required"));
@@ -218,7 +218,7 @@ export function registerBrowserActionInputCommands(
.action(async (key: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserAct(
baseUrl,
@@ -248,7 +248,7 @@ export function registerBrowserActionInputCommands(
.action(async (ref: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserAct(
baseUrl,
@@ -279,7 +279,7 @@ export function registerBrowserActionInputCommands(
.action(async (startRef: string, endRef: string, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserAct(
baseUrl,
@@ -311,7 +311,7 @@ export function registerBrowserActionInputCommands(
.action(async (ref: string, values: string[], opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserAct(
baseUrl,
@@ -350,7 +350,7 @@ export function registerBrowserActionInputCommands(
.action(async (paths: string[], opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserArmFileChooser(baseUrl, {
paths,
@@ -383,7 +383,7 @@ export function registerBrowserActionInputCommands(
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const fields = await readFields({
fields: opts.fields,
@@ -424,7 +424,7 @@ export function registerBrowserActionInputCommands(
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
const accept = opts.accept ? true : opts.dismiss ? false : undefined;
if (accept === undefined) {
defaultRuntime.error(danger("Specify --accept or --dismiss"));
@@ -462,7 +462,7 @@ export function registerBrowserActionInputCommands(
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserAct(
baseUrl,
@@ -495,7 +495,7 @@ export function registerBrowserActionInputCommands(
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
if (!opts.fn) {
defaultRuntime.error(danger("Missing --fn"));
defaultRuntime.exit(1);

View File

@@ -20,7 +20,7 @@ export function registerBrowserActionObserveCommands(
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserConsoleMessages(baseUrl, {
level: opts.level?.trim() || undefined,
@@ -45,7 +45,7 @@ export function registerBrowserActionObserveCommands(
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserPdfSave(baseUrl, {
targetId: opts.targetId?.trim() || undefined,

View File

@@ -24,7 +24,7 @@ export function registerBrowserInspectCommands(
.action(async (targetId: string | undefined, opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserScreenshotAction(baseUrl, {
targetId: targetId?.trim() || undefined,
@@ -59,7 +59,7 @@ export function registerBrowserInspectCommands(
.action(async (opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
const format = opts.format === "aria" ? "aria" : "ai";
try {
const result = await browserSnapshot(baseUrl, {

View File

@@ -31,7 +31,7 @@ export function registerBrowserManageCommands(
const baseUrl = resolveBrowserControlUrl(parent?.url);
try {
const status = await browserStatus(baseUrl, {
profile: parent?.profile,
profile: parent?.browserProfile,
});
if (parent?.json) {
defaultRuntime.log(JSON.stringify(status, null, 2));
@@ -61,7 +61,7 @@ export function registerBrowserManageCommands(
.action(async (_opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
await browserStart(baseUrl, { profile });
const status = await browserStatus(baseUrl, { profile });
@@ -85,7 +85,7 @@ export function registerBrowserManageCommands(
.action(async (_opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
await browserStop(baseUrl, { profile });
const status = await browserStatus(baseUrl, { profile });
@@ -109,7 +109,7 @@ export function registerBrowserManageCommands(
.action(async (_opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const result = await browserResetProfile(baseUrl, { profile });
if (parent?.json) {
@@ -134,7 +134,7 @@ export function registerBrowserManageCommands(
.action(async (_opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const tabs = await browserTabs(baseUrl, { profile });
if (parent?.json) {
@@ -166,7 +166,7 @@ export function registerBrowserManageCommands(
.action(async (url: string, _opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
const tab = await browserOpenTab(baseUrl, url, { profile });
if (parent?.json) {
@@ -187,7 +187,7 @@ export function registerBrowserManageCommands(
.action(async (targetId: string, _opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
await browserFocusTab(baseUrl, targetId, { profile });
if (parent?.json) {
@@ -208,7 +208,7 @@ export function registerBrowserManageCommands(
.action(async (targetId: string | undefined, _opts, cmd) => {
const parent = parentOpts(cmd);
const baseUrl = resolveBrowserControlUrl(parent?.url);
const profile = parent?.profile;
const profile = parent?.browserProfile;
try {
if (targetId?.trim()) {
await browserCloseTab(baseUrl, targetId.trim(), { profile });

View File

@@ -1,5 +1,5 @@
export type BrowserParentOpts = {
url?: string;
json?: boolean;
profile?: string;
browserProfile?: string;
};

View File

@@ -0,0 +1,79 @@
import { describe, expect, it } from "vitest";
import { Command } from "commander";
describe("browser CLI --browser-profile flag", () => {
it("parses --browser-profile from parent command options", () => {
const program = new Command();
program.name("test");
const browser = program
.command("browser")
.option("--browser-profile <name>", "Browser profile name");
let capturedProfile: string | undefined;
browser.command("status").action((_opts, cmd) => {
const parent = cmd.parent?.opts?.() as { browserProfile?: string };
capturedProfile = parent?.browserProfile;
});
program.parse(["node", "test", "browser", "--browser-profile", "onasset", "status"]);
expect(capturedProfile).toBe("onasset");
});
it("defaults to undefined when --browser-profile not provided", () => {
const program = new Command();
program.name("test");
const browser = program
.command("browser")
.option("--browser-profile <name>", "Browser profile name");
let capturedProfile: string | undefined = "should-be-undefined";
browser.command("status").action((_opts, cmd) => {
const parent = cmd.parent?.opts?.() as { browserProfile?: string };
capturedProfile = parent?.browserProfile;
});
program.parse(["node", "test", "browser", "status"]);
expect(capturedProfile).toBeUndefined();
});
it("does not conflict with global --profile flag", () => {
// The global --profile flag is handled by entry.js before Commander
// This test verifies --browser-profile is a separate option
const program = new Command();
program.name("test");
program.option("--profile <name>", "Global config profile");
const browser = program
.command("browser")
.option("--browser-profile <name>", "Browser profile name");
let globalProfile: string | undefined;
let browserProfile: string | undefined;
browser.command("status").action((_opts, cmd) => {
const parent = cmd.parent?.opts?.() as { browserProfile?: string };
browserProfile = parent?.browserProfile;
globalProfile = program.opts().profile;
});
program.parse([
"node",
"test",
"--profile",
"dev",
"browser",
"--browser-profile",
"onasset",
"status",
]);
expect(globalProfile).toBe("dev");
expect(browserProfile).toBe("onasset");
});
});

View File

@@ -20,7 +20,7 @@ export function registerBrowserCli(program: Command) {
"--url <url>",
"Override browser control URL (default from ~/.clawdbot/clawdbot.json)",
)
.option("--profile <name>", "Browser profile name (default from config)")
.option("--browser-profile <name>", "Browser profile name (default from config)")
.option("--json", "Output machine-readable JSON", false)
.addHelpText(
"after",