From e831b07f47376c6af88ebfaf4fe92d0dd3baef71 Mon Sep 17 00:00:00 2001
From: Li Yang <76434265+hewliyang@users.noreply.github.com>
Date: Mon, 26 Jan 2026 23:08:39 +0800
Subject: [PATCH] chore(cli): save screenshots to tmp dir when no path provided
(#247)
* fix(cli): save screenshots to tmp dir when no path provided
Instead of outputting base64 to stdout (which is not useful for most CLI use cases),
screenshots without a path now save to ~/.agent-browser/tmp/screenshots/ with a
generated filename and return the path.
This makes the behavior more ergonomic for AI agents and CLI users alike.
* cleanup
* cleanup
* just revert the cargo.lock version for now
* refactor: extract getAppDir() from getSocketDir()
* docs: improve screenshot help text consistency
---
README.md | 2 +-
cli/src/output.rs | 7 +------
docs/src/app/quick-start/page.tsx | 4 ++--
skills/agent-browser/SKILL.md | 4 ++--
src/actions.ts | 21 +++++++++++++++------
src/daemon.ts | 21 ++++++++++++---------
6 files changed, 33 insertions(+), 26 deletions(-)
diff --git a/README.md b/README.md
index 4e4b8be..23a0606 100644
--- a/README.md
+++ b/README.md
@@ -74,7 +74,7 @@ agent-browser scroll
[px] # Scroll (up/down/left/right)
agent-browser scrollintoview # Scroll element into view (alias: scrollinto)
agent-browser drag # Drag and drop
agent-browser upload # Upload files
-agent-browser screenshot [path] # Take screenshot (--full for full page, base64 png to stdout if no path)
+agent-browser screenshot [path] # Take screenshot (--full for full page, saves to a temporary directory if no path)
agent-browser pdf # Save as PDF
agent-browser snapshot # Accessibility tree with refs (best for AI)
agent-browser eval # Run JavaScript
diff --git a/cli/src/output.rs b/cli/src/output.rs
index dc659f8..b486b59 100644
--- a/cli/src/output.rs
+++ b/cli/src/output.rs
@@ -280,11 +280,6 @@ pub fn print_response(resp: &Response, json_mode: bool, action: Option<&str>) {
return;
}
}
- // Screenshot base64
- if let Some(base64) = data.get("base64").and_then(|v| v.as_str()) {
- println!("{}", base64);
- return;
- }
// Path-based operations (screenshot/pdf/trace/har/download/state/video)
if let Some(path) = data.get("path").and_then(|v| v.as_str()) {
match action.unwrap_or("") {
@@ -817,7 +812,7 @@ agent-browser screenshot - Take a screenshot
Usage: agent-browser screenshot [path]
Captures a screenshot of the current page. If no path is provided,
-outputs base64-encoded image data.
+saves to a temporary directory with a generated filename.
Options:
--full, -f Capture full page (not just viewport)
diff --git a/docs/src/app/quick-start/page.tsx b/docs/src/app/quick-start/page.tsx
index 00545c6..911094d 100644
--- a/docs/src/app/quick-start/page.tsx
+++ b/docs/src/app/quick-start/page.tsx
@@ -12,8 +12,8 @@ agent-browser snapshot # Get accessibility tree with refs
agent-browser click @e2 # Click by ref from snapshot
agent-browser fill @e3 "test@example.com" # Fill by ref
agent-browser get text @e1 # Get text by ref
-agent-browser screenshot # Base64 png to stdout
-agent-browser screenshot page.png # Save to file
+agent-browser screenshot # Save to a temporary directory
+agent-browser screenshot page.png # Save to a specific path
agent-browser close`} />
Traditional selectors
diff --git a/skills/agent-browser/SKILL.md b/skills/agent-browser/SKILL.md
index 764bc43..ab3ea3c 100644
--- a/skills/agent-browser/SKILL.md
+++ b/skills/agent-browser/SKILL.md
@@ -96,8 +96,8 @@ agent-browser is checked @e1 # Check if checked
### Screenshots & PDF
```bash
-agent-browser screenshot # Screenshot to stdout
-agent-browser screenshot path.png # Save to file
+agent-browser screenshot # Save to a temporary directory
+agent-browser screenshot path.png # Save to a specific path
agent-browser screenshot --full # Full page
agent-browser pdf output.pdf # Save as PDF
```
diff --git a/src/actions.ts b/src/actions.ts
index a4a0580..6162b48 100644
--- a/src/actions.ts
+++ b/src/actions.ts
@@ -1,5 +1,8 @@
import type { Page, Frame } from 'playwright-core';
+import { mkdirSync } from 'node:fs';
+import path from 'node:path';
import type { BrowserManager, ScreencastFrame } from './browser.js';
+import { getAppDir } from './daemon.js';
import type {
Command,
Response,
@@ -561,13 +564,19 @@ async function handleScreenshot(
}
try {
- if (command.path) {
- await target.screenshot({ ...options, path: command.path });
- return successResponse(command.id, { path: command.path });
- } else {
- const buffer = await target.screenshot(options);
- return successResponse(command.id, { base64: buffer.toString('base64') });
+ let savePath = command.path;
+ if (!savePath) {
+ const ext = command.format === 'jpeg' ? 'jpg' : 'png';
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
+ const random = Math.random().toString(36).substring(2, 8);
+ const filename = `screenshot-${timestamp}-${random}.${ext}`;
+ const screenshotDir = path.join(getAppDir(), 'tmp', 'screenshots');
+ mkdirSync(screenshotDir, { recursive: true });
+ savePath = path.join(screenshotDir, filename);
}
+
+ await target.screenshot({ ...options, path: savePath });
+ return successResponse(command.id, { path: savePath });
} catch (error) {
if (command.selector) {
throw toAIFriendlyError(error, command.selector);
diff --git a/src/daemon.ts b/src/daemon.ts
index 3a3b1a4..7c32eca 100644
--- a/src/daemon.ts
+++ b/src/daemon.ts
@@ -51,27 +51,30 @@ function getPortForSession(session: string): number {
* Get the base directory for socket/pid files.
* Priority: AGENT_BROWSER_SOCKET_DIR > XDG_RUNTIME_DIR > ~/.agent-browser > tmpdir
*/
-export function getSocketDir(): string {
- // 1. Explicit override
- if (process.env.AGENT_BROWSER_SOCKET_DIR) {
- return process.env.AGENT_BROWSER_SOCKET_DIR;
- }
-
- // 2. XDG_RUNTIME_DIR (Linux standard)
+export function getAppDir(): string {
+ // 1. XDG_RUNTIME_DIR (Linux standard)
if (process.env.XDG_RUNTIME_DIR) {
return path.join(process.env.XDG_RUNTIME_DIR, 'agent-browser');
}
- // 3. Home directory fallback (like Docker Desktop's ~/.docker/run/)
+ // 2. Home directory fallback (like Docker Desktop's ~/.docker/run/)
const homeDir = os.homedir();
if (homeDir) {
return path.join(homeDir, '.agent-browser');
}
- // 4. Last resort: temp dir
+ // 3. Last resort: temp dir
return path.join(os.tmpdir(), 'agent-browser');
}
+export function getSocketDir(): string {
+ // Allow explicit override for socket directory
+ if (process.env.AGENT_BROWSER_SOCKET_DIR) {
+ return process.env.AGENT_BROWSER_SOCKET_DIR;
+ }
+ return getAppDir();
+}
+
/**
* Get the socket path for the current session (Unix) or port (Windows)
*/