fix(browser-cli): rename --profile to --browser-profile to avoid conflict with global --profile flag
This commit is contained in:
@@ -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`
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type BrowserParentOpts = {
|
||||
url?: string;
|
||||
json?: boolean;
|
||||
profile?: string;
|
||||
browserProfile?: string;
|
||||
};
|
||||
|
||||
79
src/cli/browser-cli.test.ts
Normal file
79
src/cli/browser-cli.test.ts
Normal 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");
|
||||
});
|
||||
});
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user