@crewhaus/tool-screen-capture 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +29 -0
- package/dist/index.js +42 -0
- package/package.json +12 -9
- package/src/index.test.ts +0 -99
- package/src/index.ts +0 -70
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catalog R4 `tool-screen-capture` — Section 25 BROW.
|
|
3
|
+
*
|
|
4
|
+
* `Screenshot()` tool. Calls `driver.screenshot()` (PNG bytes), encodes
|
|
5
|
+
* to base64, and returns an Anthropic image content block the model
|
|
6
|
+
* can see. Composes with the §14 `tool-image` content-block pattern —
|
|
7
|
+
* but does NOT touch the filesystem (no path-traversal surface), since
|
|
8
|
+
* the driver's screenshot is in-memory.
|
|
9
|
+
*
|
|
10
|
+
* Configurable downscale factor caps model context budget. Default
|
|
11
|
+
* keeps the image at full resolution (modern Claude vision handles
|
|
12
|
+
* 1280x720 cleanly); set `downscalePercent: 50` to halve dimensions.
|
|
13
|
+
*/
|
|
14
|
+
import type { Driver } from "@crewhaus/computer-use-driver";
|
|
15
|
+
import { CrewhausError } from "@crewhaus/errors";
|
|
16
|
+
import type { RegisteredTool } from "@crewhaus/tool-catalog";
|
|
17
|
+
export declare class ScreenCaptureError extends CrewhausError {
|
|
18
|
+
readonly name = "ScreenCaptureError";
|
|
19
|
+
constructor(message: string, cause?: unknown);
|
|
20
|
+
}
|
|
21
|
+
export type CreateScreenshotToolOptions = {
|
|
22
|
+
readonly driver: Driver;
|
|
23
|
+
/**
|
|
24
|
+
* Downscale percent (1..100). Default 100 (full size). Useful for
|
|
25
|
+
* very large viewports.
|
|
26
|
+
*/
|
|
27
|
+
readonly downscalePercent?: number;
|
|
28
|
+
};
|
|
29
|
+
export declare function createScreenshotTool(opts: CreateScreenshotToolOptions): RegisteredTool;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CrewhausError } from "@crewhaus/errors";
|
|
2
|
+
import { buildTool } from "@crewhaus/tool-builder";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
export class ScreenCaptureError extends CrewhausError {
|
|
5
|
+
name = "ScreenCaptureError";
|
|
6
|
+
constructor(message, cause) {
|
|
7
|
+
super("tool", message, cause);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const screenshotSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
/**
|
|
13
|
+
* Optional reason for the snapshot — surfaces in the trace event log
|
|
14
|
+
* but doesn't affect the captured pixels.
|
|
15
|
+
*/
|
|
16
|
+
reason: z.string().optional(),
|
|
17
|
+
})
|
|
18
|
+
.strict();
|
|
19
|
+
export function createScreenshotTool(opts) {
|
|
20
|
+
return buildTool({
|
|
21
|
+
name: "Screenshot",
|
|
22
|
+
description: "Capture the current browser viewport as a PNG image you can see. Returns an image block; pair with FindElement(description) to locate UI elements before clicking.",
|
|
23
|
+
inputSchema: screenshotSchema,
|
|
24
|
+
readOnly: true,
|
|
25
|
+
destructive: false,
|
|
26
|
+
concurrencySafe: false,
|
|
27
|
+
classifyOutput: false,
|
|
28
|
+
execute: async () => {
|
|
29
|
+
const png = await opts.driver.screenshot();
|
|
30
|
+
// For v0 we ship the PNG verbatim. Downscale path is exposed in
|
|
31
|
+
// the API but not implemented (Playwright supports `clip` for
|
|
32
|
+
// crop; downscale would need an image-processing dep — deferred).
|
|
33
|
+
const data = Buffer.from(png).toString("base64");
|
|
34
|
+
return [
|
|
35
|
+
{
|
|
36
|
+
type: "image",
|
|
37
|
+
source: { type: "base64", media_type: "image/png", data },
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crewhaus/tool-screen-capture",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Screenshot tool — returns Anthropic image content block for the BROW agent (Section 25 BROW)",
|
|
6
|
-
"main": "
|
|
7
|
-
"types": "
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
|
-
".":
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
10
13
|
},
|
|
11
14
|
"scripts": {
|
|
12
15
|
"test": "bun test src"
|
|
13
16
|
},
|
|
14
17
|
"dependencies": {
|
|
15
|
-
"@crewhaus/computer-use-driver": "0.1.
|
|
16
|
-
"@crewhaus/errors": "0.1.
|
|
17
|
-
"@crewhaus/tool-builder": "0.1.
|
|
18
|
-
"@crewhaus/tool-catalog": "0.1.
|
|
18
|
+
"@crewhaus/computer-use-driver": "0.1.5",
|
|
19
|
+
"@crewhaus/errors": "0.1.5",
|
|
20
|
+
"@crewhaus/tool-builder": "0.1.5",
|
|
21
|
+
"@crewhaus/tool-catalog": "0.1.5",
|
|
19
22
|
"zod": "^3.23.8"
|
|
20
23
|
},
|
|
21
24
|
"license": "Apache-2.0",
|
|
@@ -36,5 +39,5 @@
|
|
|
36
39
|
"publishConfig": {
|
|
37
40
|
"access": "public"
|
|
38
41
|
},
|
|
39
|
-
"files": ["
|
|
42
|
+
"files": ["dist", "README.md", "LICENSE", "NOTICE"]
|
|
40
43
|
}
|
package/src/index.test.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import type { Driver } from "@crewhaus/computer-use-driver";
|
|
3
|
-
import { CrewhausError } from "@crewhaus/errors";
|
|
4
|
-
import { ScreenCaptureError, createScreenshotTool } from "./index.js";
|
|
5
|
-
|
|
6
|
-
function stubDriver(pngBytes: Uint8Array): Driver {
|
|
7
|
-
return {
|
|
8
|
-
backend: "chromium",
|
|
9
|
-
async connect() {},
|
|
10
|
-
async goto() {},
|
|
11
|
-
async screenshot() {
|
|
12
|
-
return pngBytes;
|
|
13
|
-
},
|
|
14
|
-
async click() {},
|
|
15
|
-
async type() {},
|
|
16
|
-
async key() {},
|
|
17
|
-
async scroll() {},
|
|
18
|
-
async getViewport() {
|
|
19
|
-
return { width: 800, height: 600, devicePixelRatio: 1 };
|
|
20
|
-
},
|
|
21
|
-
async disconnect() {},
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe("createScreenshotTool", () => {
|
|
26
|
-
test("returns an Anthropic image content block with base64 PNG data (T1)", async () => {
|
|
27
|
-
const png = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
28
|
-
const driver = stubDriver(png);
|
|
29
|
-
const tool = createScreenshotTool({ driver });
|
|
30
|
-
|
|
31
|
-
const result = await tool.execute({ reason: "smoke" }, {});
|
|
32
|
-
expect(Array.isArray(result)).toBe(true);
|
|
33
|
-
if (!Array.isArray(result)) throw new Error("expected ToolResultContent array");
|
|
34
|
-
expect(result).toHaveLength(1);
|
|
35
|
-
const block = result[0];
|
|
36
|
-
if (block?.type !== "image") throw new Error("expected image block");
|
|
37
|
-
expect(block.source.type).toBe("base64");
|
|
38
|
-
expect(block.source.media_type).toBe("image/png");
|
|
39
|
-
// Decode → matches input bytes.
|
|
40
|
-
const decoded = Buffer.from(block.source.data, "base64");
|
|
41
|
-
expect(Array.from(decoded)).toEqual(Array.from(png));
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("flag profile: read-only, not destructive, classifier off (controlled output)", () => {
|
|
45
|
-
const driver = stubDriver(new Uint8Array(0));
|
|
46
|
-
const tool = createScreenshotTool({ driver });
|
|
47
|
-
expect(tool.readOnly).toBe(true);
|
|
48
|
-
expect(tool.destructive).toBe(false);
|
|
49
|
-
expect(tool.classifyOutput).toBe(false);
|
|
50
|
-
expect(tool.name).toBe("Screenshot");
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test("empty PNG still yields a well-formed image block with empty base64 data", async () => {
|
|
54
|
-
const driver = stubDriver(new Uint8Array(0));
|
|
55
|
-
const tool = createScreenshotTool({ driver });
|
|
56
|
-
const result = await tool.execute({}, {});
|
|
57
|
-
if (!Array.isArray(result)) throw new Error("expected ToolResultContent array");
|
|
58
|
-
const block = result[0];
|
|
59
|
-
if (block?.type !== "image") throw new Error("expected image block");
|
|
60
|
-
expect(block.source.data).toBe("");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("downscalePercent option is accepted (v0 ships verbatim PNG)", async () => {
|
|
64
|
-
const png = new Uint8Array([0x89, 0x50, 0x4e, 0x47]);
|
|
65
|
-
const driver = stubDriver(png);
|
|
66
|
-
const tool = createScreenshotTool({ driver, downscalePercent: 50 });
|
|
67
|
-
const result = await tool.execute({}, {});
|
|
68
|
-
if (!Array.isArray(result)) throw new Error("expected ToolResultContent array");
|
|
69
|
-
const block = result[0];
|
|
70
|
-
if (block?.type !== "image") throw new Error("expected image block");
|
|
71
|
-
// Still the original bytes — downscale is exposed but unimplemented in v0.
|
|
72
|
-
expect(Array.from(Buffer.from(block.source.data, "base64"))).toEqual(Array.from(png));
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe("ScreenCaptureError", () => {
|
|
77
|
-
test("carries the 'tool' error code, a stable name, and the message", () => {
|
|
78
|
-
const err = new ScreenCaptureError("driver lost the page");
|
|
79
|
-
expect(err).toBeInstanceOf(ScreenCaptureError);
|
|
80
|
-
expect(err).toBeInstanceOf(CrewhausError);
|
|
81
|
-
expect(err).toBeInstanceOf(Error);
|
|
82
|
-
expect(err.name).toBe("ScreenCaptureError");
|
|
83
|
-
expect(err.code).toBe("tool");
|
|
84
|
-
expect(err.message).toBe("driver lost the page");
|
|
85
|
-
expect(err.cause).toBeUndefined();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("forwards an underlying cause and serializes it via toJSON()", () => {
|
|
89
|
-
const root = new Error("ETIMEDOUT");
|
|
90
|
-
const err = new ScreenCaptureError("screenshot failed", root);
|
|
91
|
-
expect(err.cause).toBe(root);
|
|
92
|
-
expect(err.toJSON()).toEqual({
|
|
93
|
-
name: "ScreenCaptureError",
|
|
94
|
-
code: "tool",
|
|
95
|
-
message: "screenshot failed",
|
|
96
|
-
cause: { name: "Error", message: "ETIMEDOUT" },
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Catalog R4 `tool-screen-capture` — Section 25 BROW.
|
|
3
|
-
*
|
|
4
|
-
* `Screenshot()` tool. Calls `driver.screenshot()` (PNG bytes), encodes
|
|
5
|
-
* to base64, and returns an Anthropic image content block the model
|
|
6
|
-
* can see. Composes with the §14 `tool-image` content-block pattern —
|
|
7
|
-
* but does NOT touch the filesystem (no path-traversal surface), since
|
|
8
|
-
* the driver's screenshot is in-memory.
|
|
9
|
-
*
|
|
10
|
-
* Configurable downscale factor caps model context budget. Default
|
|
11
|
-
* keeps the image at full resolution (modern Claude vision handles
|
|
12
|
-
* 1280x720 cleanly); set `downscalePercent: 50` to halve dimensions.
|
|
13
|
-
*/
|
|
14
|
-
import type { Driver } from "@crewhaus/computer-use-driver";
|
|
15
|
-
import { CrewhausError } from "@crewhaus/errors";
|
|
16
|
-
import { buildTool } from "@crewhaus/tool-builder";
|
|
17
|
-
import type { RegisteredTool, ToolResultContent } from "@crewhaus/tool-catalog";
|
|
18
|
-
import { z } from "zod";
|
|
19
|
-
|
|
20
|
-
export class ScreenCaptureError extends CrewhausError {
|
|
21
|
-
override readonly name = "ScreenCaptureError";
|
|
22
|
-
constructor(message: string, cause?: unknown) {
|
|
23
|
-
super("tool", message, cause);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const screenshotSchema = z
|
|
28
|
-
.object({
|
|
29
|
-
/**
|
|
30
|
-
* Optional reason for the snapshot — surfaces in the trace event log
|
|
31
|
-
* but doesn't affect the captured pixels.
|
|
32
|
-
*/
|
|
33
|
-
reason: z.string().optional(),
|
|
34
|
-
})
|
|
35
|
-
.strict();
|
|
36
|
-
|
|
37
|
-
export type CreateScreenshotToolOptions = {
|
|
38
|
-
readonly driver: Driver;
|
|
39
|
-
/**
|
|
40
|
-
* Downscale percent (1..100). Default 100 (full size). Useful for
|
|
41
|
-
* very large viewports.
|
|
42
|
-
*/
|
|
43
|
-
readonly downscalePercent?: number;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export function createScreenshotTool(opts: CreateScreenshotToolOptions): RegisteredTool {
|
|
47
|
-
return buildTool({
|
|
48
|
-
name: "Screenshot",
|
|
49
|
-
description:
|
|
50
|
-
"Capture the current browser viewport as a PNG image you can see. Returns an image block; pair with FindElement(description) to locate UI elements before clicking.",
|
|
51
|
-
inputSchema: screenshotSchema,
|
|
52
|
-
readOnly: true,
|
|
53
|
-
destructive: false,
|
|
54
|
-
concurrencySafe: false,
|
|
55
|
-
classifyOutput: false,
|
|
56
|
-
execute: async (): Promise<ToolResultContent> => {
|
|
57
|
-
const png = await opts.driver.screenshot();
|
|
58
|
-
// For v0 we ship the PNG verbatim. Downscale path is exposed in
|
|
59
|
-
// the API but not implemented (Playwright supports `clip` for
|
|
60
|
-
// crop; downscale would need an image-processing dep — deferred).
|
|
61
|
-
const data = Buffer.from(png).toString("base64");
|
|
62
|
-
return [
|
|
63
|
-
{
|
|
64
|
-
type: "image",
|
|
65
|
-
source: { type: "base64", media_type: "image/png", data },
|
|
66
|
-
},
|
|
67
|
-
];
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
}
|