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) */