mirror of
https://github.com/samsonjs/Peekaboo.git
synced 2026-04-10 12:05:49 +00:00
fix: Prevent format 'data' for screen captures to avoid stack overflow
- Screen captures now reject format: 'data' with clear error message - Large screen images cause JavaScript stack overflow when base64 encoded - Application window captures can still use format: 'data' - Update tests and documentation to reflect this limitation
This commit is contained in:
parent
338b994ac9
commit
30277bbf6c
5 changed files with 47 additions and 10 deletions
|
|
@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Restriction on using `format: "data"` for screen captures to prevent JavaScript stack overflow errors
|
||||
- Screen captures must use `format: "png"` or omit the format parameter
|
||||
- Application window captures can still use `format: "data"`
|
||||
|
||||
## [1.0.0-beta.18] - 2025-06-08
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -289,15 +289,17 @@ Peekaboo provides three main tools for AI agents:
|
|||
|
||||
Captures macOS screen content with automatic shadow/frame removal.
|
||||
|
||||
**Important:** Screen captures cannot use `format: "data"` due to the large size of screen images causing JavaScript stack overflow errors. Always use `format: "png"` (or omit format) with a `path` for screen captures.
|
||||
|
||||
**Examples:**
|
||||
```javascript
|
||||
// Capture entire screen
|
||||
// Capture entire screen (must save to file)
|
||||
await use_mcp_tool("peekaboo", "image", {
|
||||
app_target: "screen:0",
|
||||
path: "~/Desktop/screenshot.png"
|
||||
});
|
||||
|
||||
// Capture specific app window with analysis
|
||||
// Capture specific app window with analysis (can use format: "data")
|
||||
await use_mcp_tool("peekaboo", "image", {
|
||||
app_target: "Safari",
|
||||
question: "What website is currently open?",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// This file is auto-generated by the build script. Do not edit manually.
|
||||
enum Version {
|
||||
static let current = "1.0.0-beta.17"
|
||||
static let current = "1.0.0-beta.18"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,23 @@ export async function imageToolHandler(
|
|||
try {
|
||||
logger.debug({ input }, "Processing peekaboo.image tool call");
|
||||
|
||||
// Validate format restrictions for screen captures
|
||||
const isScreenCapture = !input.app_target || input.app_target.startsWith("screen:");
|
||||
if (isScreenCapture && input.format === "data") {
|
||||
logger.warn("Screen capture with format 'data' is not allowed due to size constraints");
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Screen captures cannot use format 'data' because they produce images too large for base64 encoding. " +
|
||||
"Please use format 'png' to save to a file instead.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
_meta: { backend_error_code: "FORMAT_NOT_ALLOWED_FOR_SCREEN" },
|
||||
};
|
||||
}
|
||||
|
||||
// Determine effective path and format for Swift CLI
|
||||
const swiftFormat = input.format === "data" ? "png" : (input.format || "png");
|
||||
|
||||
|
|
|
|||
|
|
@ -109,14 +109,26 @@ describe("Image Tool", () => {
|
|||
expect(mockFsRm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should capture screen with format: 'data'", async () => {
|
||||
it("should reject screen capture with format: 'data'", async () => {
|
||||
const result = await imageToolHandler(
|
||||
{ format: "data" },
|
||||
mockContext,
|
||||
);
|
||||
|
||||
expect(result.isError).toBe(true);
|
||||
expect(result.content[0].text).toContain("Screen captures cannot use format 'data'");
|
||||
expect(result._meta?.backend_error_code).toBe("FORMAT_NOT_ALLOWED_FOR_SCREEN");
|
||||
expect(mockExecuteSwiftCli).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should allow app capture with format: 'data'", async () => {
|
||||
// Mock resolveImagePath to return a temp directory for format: "data"
|
||||
mockResolveImagePath.mockResolvedValue({
|
||||
effectivePath: MOCK_TEMP_IMAGE_DIR,
|
||||
tempDirUsed: MOCK_TEMP_IMAGE_DIR,
|
||||
});
|
||||
|
||||
const mockResponse = mockSwiftCli.captureImage("screen", {
|
||||
const mockResponse = mockSwiftCli.captureImage("Safari", {
|
||||
path: MOCK_SAVED_FILE_PATH,
|
||||
format: "png",
|
||||
});
|
||||
|
|
@ -124,7 +136,7 @@ describe("Image Tool", () => {
|
|||
mockReadImageAsBase64.mockResolvedValue("base64imagedata");
|
||||
|
||||
const result = await imageToolHandler(
|
||||
{ format: "data" },
|
||||
{ app_target: "Safari", format: "data" },
|
||||
mockContext,
|
||||
);
|
||||
|
||||
|
|
@ -140,7 +152,7 @@ describe("Image Tool", () => {
|
|||
expect(mockFsRm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should save file and return base64 when format: 'data' with path", async () => {
|
||||
it("should save file and return base64 when format: 'data' with path for app capture", async () => {
|
||||
const userPath = "/user/test.png";
|
||||
// Mock resolveImagePath to return the user path (no temp dir)
|
||||
mockResolveImagePath.mockResolvedValue({
|
||||
|
|
@ -151,7 +163,7 @@ describe("Image Tool", () => {
|
|||
const mockSavedFile: SavedFile = {
|
||||
path: userPath,
|
||||
mime_type: "image/png",
|
||||
item_label: "Screen 1",
|
||||
item_label: "Safari",
|
||||
};
|
||||
const mockResponse = {
|
||||
success: true,
|
||||
|
|
@ -162,7 +174,7 @@ describe("Image Tool", () => {
|
|||
mockReadImageAsBase64.mockResolvedValue("base64imagedata");
|
||||
|
||||
const result = await imageToolHandler(
|
||||
{ format: "data", path: userPath },
|
||||
{ app_target: "Safari", format: "data", path: userPath },
|
||||
mockContext,
|
||||
);
|
||||
|
||||
|
|
@ -644,7 +656,7 @@ describe("Image Tool", () => {
|
|||
tempDirUsed: MOCK_TEMP_IMAGE_DIR,
|
||||
});
|
||||
|
||||
const mockCliResponse = mockSwiftCli.captureImage("screen", {
|
||||
const mockCliResponse = mockSwiftCli.captureImage("Safari", {
|
||||
path: MOCK_SAVED_FILE_PATH,
|
||||
format: "png",
|
||||
});
|
||||
|
|
@ -652,6 +664,7 @@ describe("Image Tool", () => {
|
|||
|
||||
const result = await imageToolHandler(
|
||||
{
|
||||
app_target: "Safari", // Use app capture to allow format: "data"
|
||||
question: MOCK_QUESTION,
|
||||
format: "data", // Even with format: "data"
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue