Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-pdf-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@browserbasehq/browse-cli": patch
---

Add `browse pdf [path]` command to save the current page as a PDF. Supports `--landscape`, `--format` (letter, legal, a4, a3), `--scale`, and `--no-background` options. Returns base64-encoded PDF when no path is given.
86 changes: 86 additions & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,52 @@ async function executeCommand(
}
}

// PDF
case "pdf": {
if (!page) {
return { error: "No page open. Navigate to a URL first." };
}
const [opts] = args as [
{
path?: string;
landscape?: boolean;
printBackground?: boolean;
scale?: number;
format?: string;
}?,
];
const cdpSession = page.mainFrame().session;
const paperSizes: Record<string, { width: number; height: number }> = {
letter: { width: 8.5, height: 11 },
legal: { width: 8.5, height: 14 },
a4: { width: 8.27, height: 11.69 },
a3: { width: 11.69, height: 16.54 },
};
const paper =
paperSizes[(opts?.format ?? "letter").toLowerCase()] ??
paperSizes.letter;
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
const result = await cdpSession.send<{ data: string }>(
"Page.printToPDF",
{
landscape: opts?.landscape ?? false,
printBackground: opts?.printBackground ?? true,
scale: opts?.scale ?? 1,
paperWidth: paper.width,
paperHeight: paper.height,
marginTop: 0.4,
marginBottom: 0.4,
marginLeft: 0.4,
marginRight: 0.4,
preferCSSPageSize: true,
},
);
if (opts?.path) {
await fs.writeFile(opts.path, Buffer.from(result.data, "base64"));
return { saved: opts.path };
}
return { base64: result.data };
}

// Daemon control
case "stop": {
process.nextTick(() => {
Expand Down Expand Up @@ -2756,6 +2802,46 @@ networkCmd
}
});

// ==================== PDF ====================

program
.command("pdf [path]")
.description("Save page as PDF")
.option("--landscape", "Landscape orientation")
.option("--no-background", "Omit background graphics")
.option("--scale <n>", "Scale factor (0.1-2)", "1")
.option("--format <format>", "Paper format: letter, legal, a4, a3", "letter")
.action(async (filePath: string | undefined, cmdOpts) => {
const opts = program.opts<GlobalOpts>();
try {
const scale = parseFloat(cmdOpts.scale);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Scale validation is too permissive because parseFloat accepts invalid trailing characters (e.g. 1abc). Use strict numeric parsing so malformed --scale values are rejected.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/index.ts, line 2814:

<comment>Scale validation is too permissive because `parseFloat` accepts invalid trailing characters (e.g. `1abc`). Use strict numeric parsing so malformed `--scale` values are rejected.</comment>

<file context>
@@ -2811,12 +2811,24 @@ program
   .action(async (filePath: string | undefined, cmdOpts) => {
     const opts = program.opts<GlobalOpts>();
     try {
+      const scale = parseFloat(cmdOpts.scale);
+      if (isNaN(scale) || scale < 0.1 || scale > 2) {
+        console.error("Error: --scale must be a number between 0.1 and 2");
</file context>
Fix with Cubic

if (isNaN(scale) || scale < 0.1 || scale > 2) {
console.error("Error: --scale must be a number between 0.1 and 2");
process.exit(1);
}
const validFormats = ["letter", "legal", "a4", "a3"];
if (!validFormats.includes(cmdOpts.format.toLowerCase())) {
console.error(
`Error: --format must be one of: ${validFormats.join(", ")}`,
);
process.exit(1);
}
const result = await runCommand("pdf", [
{
path: filePath,
landscape: cmdOpts.landscape,
printBackground: cmdOpts.background !== false,
scale,
format: cmdOpts.format,
},
]);
output(result, opts.json ?? false);
} catch (e) {
console.error("Error:", e instanceof Error ? e.message : e);
process.exit(1);
}
});

// ==================== RUN ====================

program.parse();
Loading