@ingcreators/annot-mcp 0.1.0 → 0.2.0
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/CHANGELOG.md +53 -0
- package/README.md +6 -10
- package/dist/dsl/schema.d.ts +44 -184
- package/dist/dsl/types.d.ts +7 -68
- package/dist/index.d.ts +1 -1
- package/dist/index.js +727 -815
- package/dist/io/encode-output.d.ts +29 -0
- package/dist/tools/annotate-screenshot.d.ts +41 -4
- package/dist/tools/annotate-url.d.ts +53 -16
- package/dist/tools/compare-screenshots.d.ts +38 -0
- package/dist/tools/redact-screenshot.d.ts +41 -4
- package/dist/tools/redact-url.d.ts +40 -4
- package/package.json +2 -2
- package/dist/dsl/svg-primitives.d.ts +0 -34
- package/dist/dsl/to-svg.d.ts +0 -10
package/dist/index.js
CHANGED
|
@@ -1,56 +1,57 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { SHARED_DEFS as te, DEFAULT_ENCODE_OPTIONS as oe, decodeAndEncodeImage as re, BBOX_ANNOTATION_SCHEMA as ne, bboxAnnotationsToSvg as U, BBOX_REDACT_REGION_SCHEMA as se, createAnnotator as ie } from "@ingcreators/annot-annotator";
|
|
2
|
+
import { BBOX_ANNOTATION_SCHEMA as At, BBOX_REDACT_REGION_SCHEMA as Tt, arrowBetween as Rt, bboxAnnotationsToSvg as It, rectForBoundingBox as _t, textAt as Ct } from "@ingcreators/annot-annotator";
|
|
3
|
+
import { loadImage as C, createCanvas as F } from "@napi-rs/canvas";
|
|
4
|
+
import ae from "pixelmatch";
|
|
5
|
+
import { readFile as ce, writeFile as O } from "node:fs/promises";
|
|
6
|
+
import { isAbsolute as b } from "node:path";
|
|
7
|
+
import { Server as de } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
|
+
import { ListToolsRequestSchema as le, CallToolRequestSchema as ue } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { StdioServerTransport as he } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
|
+
const fe = { width: 1280, height: 800, deviceScaleFactor: 1 }, pe = "load";
|
|
11
|
+
async function W(e, t) {
|
|
12
|
+
const o = { ...fe, ...t.viewport }, n = t.waitFor ?? pe, s = t.fullPage ?? !1, c = await e.acquire();
|
|
13
|
+
let r, a = !1;
|
|
14
|
+
const i = () => {
|
|
15
|
+
a || (a = !0, e.release());
|
|
15
16
|
};
|
|
16
17
|
try {
|
|
17
|
-
|
|
18
|
-
viewport: { width:
|
|
19
|
-
deviceScaleFactor:
|
|
18
|
+
r = await c.newContext({
|
|
19
|
+
viewport: { width: o.width, height: o.height },
|
|
20
|
+
deviceScaleFactor: o.deviceScaleFactor
|
|
20
21
|
});
|
|
21
|
-
const
|
|
22
|
-
await
|
|
23
|
-
const
|
|
24
|
-
return { pngBytes:
|
|
25
|
-
page:
|
|
22
|
+
const d = await r.newPage();
|
|
23
|
+
await d.goto(t.url, { waitUntil: n });
|
|
24
|
+
const l = await d.screenshot({ fullPage: s, type: "png" }), v = new Uint8Array(l.buffer, l.byteOffset, l.byteLength), u = r;
|
|
25
|
+
return { pngBytes: v, handle: {
|
|
26
|
+
page: d,
|
|
26
27
|
async close() {
|
|
27
28
|
if (!a)
|
|
28
29
|
try {
|
|
29
|
-
await
|
|
30
|
+
await u.close();
|
|
30
31
|
} finally {
|
|
31
|
-
|
|
32
|
+
i();
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
} };
|
|
35
|
-
} catch (
|
|
36
|
-
if (
|
|
36
|
+
} catch (d) {
|
|
37
|
+
if (r)
|
|
37
38
|
try {
|
|
38
|
-
await
|
|
39
|
+
await r.close();
|
|
39
40
|
} catch {
|
|
40
41
|
}
|
|
41
|
-
throw
|
|
42
|
+
throw i(), d;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
const
|
|
45
|
-
class
|
|
46
|
-
#i;
|
|
45
|
+
const me = 3e4;
|
|
46
|
+
class ge {
|
|
47
47
|
#s;
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#e = 0;
|
|
48
|
+
#i;
|
|
49
|
+
#e;
|
|
51
50
|
#r;
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
#t = 0;
|
|
52
|
+
#o;
|
|
53
|
+
constructor(t, o = {}) {
|
|
54
|
+
this.#s = t, this.#i = o.idleTimeoutMs ?? me;
|
|
54
55
|
}
|
|
55
56
|
/**
|
|
56
57
|
* Acquire a browser instance. The pool launches lazily on the
|
|
@@ -63,9 +64,9 @@ class gt {
|
|
|
63
64
|
* accurately.
|
|
64
65
|
*/
|
|
65
66
|
async acquire() {
|
|
66
|
-
return this.#n(), this.#
|
|
67
|
-
throw this.#
|
|
68
|
-
})), this.#
|
|
67
|
+
return this.#n(), this.#t += 1, this.#e ? this.#e : (this.#r || (this.#r = this.#s.launch().then((t) => (this.#e = t, this.#r = void 0, t)).catch((t) => {
|
|
68
|
+
throw this.#r = void 0, this.#t = Math.max(0, this.#t - 1), t;
|
|
69
|
+
})), this.#r);
|
|
69
70
|
}
|
|
70
71
|
/**
|
|
71
72
|
* Release a previously-acquired browser. Decrements the
|
|
@@ -74,7 +75,7 @@ class gt {
|
|
|
74
75
|
* instance; otherwise the timer fires and the browser closes.
|
|
75
76
|
*/
|
|
76
77
|
release() {
|
|
77
|
-
this.#
|
|
78
|
+
this.#t !== 0 && (this.#t -= 1, this.#t === 0 && this.#e && this.#a());
|
|
78
79
|
}
|
|
79
80
|
/**
|
|
80
81
|
* Close the pooled browser immediately, regardless of refcount.
|
|
@@ -83,349 +84,239 @@ class gt {
|
|
|
83
84
|
*/
|
|
84
85
|
async shutdown() {
|
|
85
86
|
this.#n();
|
|
86
|
-
const
|
|
87
|
-
this.#
|
|
87
|
+
const t = this.#e;
|
|
88
|
+
this.#e = void 0, this.#t = 0, t && await t.close();
|
|
88
89
|
}
|
|
89
90
|
/** Refcount of outstanding borrows. Exposed for tests / metrics. */
|
|
90
91
|
get activeBorrows() {
|
|
91
|
-
return this.#
|
|
92
|
+
return this.#t;
|
|
92
93
|
}
|
|
93
94
|
/** Whether a browser is currently launched. Exposed for tests. */
|
|
94
95
|
get isLaunched() {
|
|
95
|
-
return this.#
|
|
96
|
+
return this.#e !== void 0;
|
|
96
97
|
}
|
|
97
98
|
// ─── internals ────────────────────────────────────────────────
|
|
98
99
|
#n() {
|
|
99
|
-
this.#
|
|
100
|
+
this.#o && (clearTimeout(this.#o), this.#o = void 0);
|
|
100
101
|
}
|
|
101
102
|
#a() {
|
|
102
|
-
this.#n(), this.#
|
|
103
|
-
const
|
|
104
|
-
this.#
|
|
103
|
+
this.#n(), this.#o = setTimeout(() => {
|
|
104
|
+
const t = this.#e;
|
|
105
|
+
this.#e = void 0, this.#o = void 0, t?.close().catch(() => {
|
|
105
106
|
});
|
|
106
|
-
}, this.#
|
|
107
|
+
}, this.#i), this.#o.unref?.();
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
|
-
function
|
|
110
|
-
return new
|
|
110
|
+
function ye(e = {}) {
|
|
111
|
+
return new ge(
|
|
111
112
|
{
|
|
112
113
|
async launch() {
|
|
113
|
-
let
|
|
114
|
+
let t;
|
|
114
115
|
try {
|
|
115
|
-
({ chromium:
|
|
116
|
-
} catch (
|
|
117
|
-
throw new
|
|
116
|
+
({ chromium: t } = await import("playwright-core"));
|
|
117
|
+
} catch (o) {
|
|
118
|
+
throw new q(
|
|
118
119
|
"Failed to load `playwright-core`. Reinstall `@ingcreators/annot-mcp` to restore the dependency.",
|
|
119
|
-
|
|
120
|
+
o
|
|
120
121
|
);
|
|
121
122
|
}
|
|
122
123
|
try {
|
|
123
|
-
return await
|
|
124
|
-
} catch (
|
|
125
|
-
throw new
|
|
124
|
+
return await t.launch({ headless: !0 });
|
|
125
|
+
} catch (o) {
|
|
126
|
+
throw new q(
|
|
126
127
|
"Failed to launch Chromium. Run `npx playwright install chromium` to download the runtime, then retry.",
|
|
127
|
-
|
|
128
|
+
o
|
|
128
129
|
);
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
},
|
|
132
|
-
|
|
133
|
+
e
|
|
133
134
|
);
|
|
134
135
|
}
|
|
135
|
-
class
|
|
136
|
+
class q extends Error {
|
|
136
137
|
cause;
|
|
137
|
-
constructor(
|
|
138
|
-
super(
|
|
138
|
+
constructor(t, o) {
|
|
139
|
+
super(t), this.name = "ChromiumUnavailableError", this.cause = o;
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
|
-
class
|
|
142
|
+
class N extends Error {
|
|
142
143
|
locator;
|
|
143
|
-
constructor(
|
|
144
|
-
super(
|
|
144
|
+
constructor(t, o) {
|
|
145
|
+
super(o), this.name = "LocatorResolutionError", this.locator = t;
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
|
-
async function f(
|
|
148
|
-
const
|
|
149
|
-
if (!
|
|
150
|
-
throw new
|
|
151
|
-
|
|
152
|
-
`Locator "${
|
|
148
|
+
async function f(e, t) {
|
|
149
|
+
const o = await e.locator(t).boundingBox();
|
|
150
|
+
if (!o)
|
|
151
|
+
throw new N(
|
|
152
|
+
t,
|
|
153
|
+
`Locator "${t}" resolved to no visible element. Check the selector matches at least one element and the element is inside the captured viewport.`
|
|
153
154
|
);
|
|
154
|
-
return
|
|
155
|
+
return o;
|
|
155
156
|
}
|
|
156
|
-
async function
|
|
157
|
-
switch (
|
|
157
|
+
async function we(e, t) {
|
|
158
|
+
switch (t.type) {
|
|
158
159
|
case "rect": {
|
|
159
|
-
const
|
|
160
|
-
return { ...
|
|
160
|
+
const o = t.bbox ?? await f(e, m(t.locator));
|
|
161
|
+
return { ...g(t), type: "rect", bbox: o };
|
|
161
162
|
}
|
|
162
163
|
case "circle": {
|
|
163
|
-
if (
|
|
164
|
+
if (t.center !== void 0 && t.radius !== void 0)
|
|
164
165
|
return {
|
|
165
|
-
...
|
|
166
|
+
...g(t),
|
|
166
167
|
type: "circle",
|
|
167
|
-
center:
|
|
168
|
-
radius:
|
|
168
|
+
center: t.center,
|
|
169
|
+
radius: t.radius
|
|
169
170
|
};
|
|
170
|
-
const
|
|
171
|
+
const o = await f(e, m(t.locator));
|
|
171
172
|
return {
|
|
172
|
-
...
|
|
173
|
+
...g(t),
|
|
173
174
|
type: "circle",
|
|
174
|
-
center:
|
|
175
|
-
radius: Math.min(
|
|
175
|
+
center: T(o),
|
|
176
|
+
radius: Math.min(o.width, o.height) / 2
|
|
176
177
|
};
|
|
177
178
|
}
|
|
178
179
|
case "arrow": {
|
|
179
|
-
const
|
|
180
|
+
const o = t.from ?? T(await f(e, m(t.fromLocator))), n = t.to ?? T(await f(e, m(t.toLocator)));
|
|
180
181
|
return {
|
|
181
|
-
...
|
|
182
|
+
...g(t),
|
|
182
183
|
type: "arrow",
|
|
183
|
-
from:
|
|
184
|
-
to:
|
|
184
|
+
from: o,
|
|
185
|
+
to: n
|
|
185
186
|
};
|
|
186
187
|
}
|
|
187
188
|
case "text": {
|
|
188
|
-
const
|
|
189
|
-
await f(
|
|
190
|
-
|
|
191
|
-
),
|
|
192
|
-
...
|
|
189
|
+
const o = t.at ?? $e(
|
|
190
|
+
await f(e, m(t.locator)),
|
|
191
|
+
t.fontSize
|
|
192
|
+
), n = {
|
|
193
|
+
...g(t),
|
|
193
194
|
type: "text",
|
|
194
|
-
at:
|
|
195
|
-
content:
|
|
195
|
+
at: o,
|
|
196
|
+
content: t.content
|
|
196
197
|
};
|
|
197
|
-
return
|
|
198
|
+
return t.fontSize !== void 0 && (n.fontSize = t.fontSize), t.anchor !== void 0 && (n.anchor = t.anchor), n;
|
|
198
199
|
}
|
|
199
200
|
case "callout": {
|
|
200
|
-
const
|
|
201
|
+
const o = t.at ?? await ve(e, t.atLocator), n = t.targetBbox ?? await f(e, m(t.targetLocator));
|
|
201
202
|
return {
|
|
202
|
-
...
|
|
203
|
+
...g(t),
|
|
203
204
|
type: "callout",
|
|
204
|
-
at:
|
|
205
|
-
targetBbox:
|
|
206
|
-
content:
|
|
205
|
+
at: o,
|
|
206
|
+
targetBbox: n,
|
|
207
|
+
content: t.content
|
|
207
208
|
};
|
|
208
209
|
}
|
|
209
210
|
case "raw":
|
|
210
|
-
return
|
|
211
|
+
return t;
|
|
211
212
|
}
|
|
212
213
|
}
|
|
213
|
-
async function
|
|
214
|
-
const
|
|
215
|
-
for (const
|
|
216
|
-
|
|
217
|
-
return
|
|
214
|
+
async function be(e, t) {
|
|
215
|
+
const o = [];
|
|
216
|
+
for (const n of t)
|
|
217
|
+
o.push(await we(e, n));
|
|
218
|
+
return o;
|
|
218
219
|
}
|
|
219
|
-
async function
|
|
220
|
-
if (!
|
|
221
|
-
throw new
|
|
220
|
+
async function ve(e, t) {
|
|
221
|
+
if (!t)
|
|
222
|
+
throw new N(
|
|
222
223
|
"<callout.at>",
|
|
223
224
|
"callout annotations require either `at` or `atLocator`."
|
|
224
225
|
);
|
|
225
|
-
const
|
|
226
|
-
return
|
|
226
|
+
const o = await f(e, t);
|
|
227
|
+
return T(o);
|
|
227
228
|
}
|
|
228
|
-
function
|
|
229
|
-
return { x:
|
|
229
|
+
function T(e) {
|
|
230
|
+
return { x: e.x + e.width / 2, y: e.y + e.height / 2 };
|
|
230
231
|
}
|
|
231
|
-
function
|
|
232
|
-
const
|
|
233
|
-
return { x:
|
|
232
|
+
function $e(e, t) {
|
|
233
|
+
const o = (t ?? 14) + 2;
|
|
234
|
+
return { x: e.x, y: Math.max(0, e.y - o) };
|
|
234
235
|
}
|
|
235
|
-
function
|
|
236
|
-
if (
|
|
237
|
-
throw new
|
|
236
|
+
function m(e) {
|
|
237
|
+
if (e === void 0)
|
|
238
|
+
throw new N(
|
|
238
239
|
"<missing>",
|
|
239
240
|
"Internal error: annotation passed schema validation without a locator or bbox. This is a bug."
|
|
240
241
|
);
|
|
241
|
-
return
|
|
242
|
+
return e;
|
|
242
243
|
}
|
|
243
|
-
function
|
|
244
|
-
if (
|
|
244
|
+
function g(e) {
|
|
245
|
+
if (e.type === "raw")
|
|
245
246
|
return {};
|
|
246
|
-
const
|
|
247
|
-
return
|
|
248
|
-
}
|
|
249
|
-
const
|
|
250
|
-
function
|
|
251
|
-
const
|
|
252
|
-
for (let
|
|
253
|
-
for (let
|
|
254
|
-
const a =
|
|
255
|
-
if (
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
x:
|
|
259
|
-
y:
|
|
260
|
-
width:
|
|
261
|
-
height:
|
|
247
|
+
const t = {};
|
|
248
|
+
return e.intent !== void 0 && (t.intent = e.intent), e.stroke !== void 0 && (t.stroke = e.stroke), e.strokeWidth !== void 0 && (t.strokeWidth = e.strokeWidth), e.fill !== void 0 && (t.fill = e.fill), e.color !== void 0 && (t.color = e.color), t;
|
|
249
|
+
}
|
|
250
|
+
const Ee = 4;
|
|
251
|
+
function xe(e, t, o) {
|
|
252
|
+
const n = new Uint8Array(t * o), s = [];
|
|
253
|
+
for (let c = 0; c < o; c++)
|
|
254
|
+
for (let r = 0; r < t; r++) {
|
|
255
|
+
const a = c * t + r;
|
|
256
|
+
if (n[a] || !X(e, a)) continue;
|
|
257
|
+
const i = Oe(e, n, r, c, t, o);
|
|
258
|
+
i.count >= Ee && s.push({
|
|
259
|
+
x: i.minX,
|
|
260
|
+
y: i.minY,
|
|
261
|
+
width: i.maxX - i.minX + 1,
|
|
262
|
+
height: i.maxY - i.minY + 1
|
|
262
263
|
});
|
|
263
264
|
}
|
|
264
|
-
return
|
|
265
|
+
return s;
|
|
265
266
|
}
|
|
266
|
-
function X(
|
|
267
|
-
return t
|
|
267
|
+
function X(e, t) {
|
|
268
|
+
return e[t * 4 + 3] > 0;
|
|
268
269
|
}
|
|
269
|
-
function
|
|
270
|
-
const
|
|
270
|
+
function Oe(e, t, o, n, s, c) {
|
|
271
|
+
const r = {
|
|
271
272
|
count: 0,
|
|
272
|
-
minX:
|
|
273
|
-
minY:
|
|
274
|
-
maxX:
|
|
275
|
-
maxY:
|
|
276
|
-
}, a = [
|
|
273
|
+
minX: o,
|
|
274
|
+
minY: n,
|
|
275
|
+
maxX: o,
|
|
276
|
+
maxY: n
|
|
277
|
+
}, a = [o, n];
|
|
277
278
|
for (; a.length > 0; ) {
|
|
278
|
-
const
|
|
279
|
-
if (
|
|
280
|
-
const
|
|
281
|
-
|
|
279
|
+
const i = a.pop(), d = a.pop();
|
|
280
|
+
if (d < 0 || d >= s || i < 0 || i >= c) continue;
|
|
281
|
+
const l = i * s + d;
|
|
282
|
+
t[l] || X(e, l) && (t[l] = 1, r.count += 1, d < r.minX && (r.minX = d), i < r.minY && (r.minY = i), d > r.maxX && (r.maxX = d), i > r.maxY && (r.maxY = i), a.push(d + 1, i), a.push(d - 1, i), a.push(d, i + 1), a.push(d, i - 1));
|
|
282
283
|
}
|
|
283
|
-
return
|
|
284
|
+
return r;
|
|
284
285
|
}
|
|
285
|
-
class
|
|
286
|
-
constructor(
|
|
286
|
+
class Pe extends Error {
|
|
287
|
+
constructor(t, o) {
|
|
287
288
|
super(
|
|
288
|
-
`Cannot compare screenshots of different dimensions: before is ${
|
|
289
|
+
`Cannot compare screenshots of different dimensions: before is ${t.width}×${t.height}, after is ${o.width}×${o.height}.`
|
|
289
290
|
), this.name = "DimensionMismatchError";
|
|
290
291
|
}
|
|
291
292
|
}
|
|
292
|
-
async function
|
|
293
|
-
const [
|
|
294
|
-
|
|
295
|
-
|
|
293
|
+
async function Se(e, t, o = {}) {
|
|
294
|
+
const [n, s] = await Promise.all([
|
|
295
|
+
C(Buffer.from(e)),
|
|
296
|
+
C(Buffer.from(t))
|
|
296
297
|
]);
|
|
297
|
-
if (
|
|
298
|
-
throw new
|
|
299
|
-
{ width:
|
|
300
|
-
{ width:
|
|
298
|
+
if (n.width !== s.width || n.height !== s.height)
|
|
299
|
+
throw new Pe(
|
|
300
|
+
{ width: n.width, height: n.height },
|
|
301
|
+
{ width: s.width, height: s.height }
|
|
301
302
|
);
|
|
302
|
-
const { width:
|
|
303
|
-
threshold:
|
|
303
|
+
const { width: c, height: r } = n, a = k(n, c, r), i = k(s, c, r), d = new Uint8Array(c * r * 4), l = ae(a, i, d, c, r, {
|
|
304
|
+
threshold: o.threshold ?? 0.1,
|
|
304
305
|
includeAA: !1,
|
|
305
306
|
diffMask: !0
|
|
306
|
-
}),
|
|
307
|
-
return { mismatchedPixels:
|
|
307
|
+
}), v = xe(d, c, r);
|
|
308
|
+
return { mismatchedPixels: l, regions: v, width: c, height: r };
|
|
308
309
|
}
|
|
309
|
-
function
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
return new Uint8Array(
|
|
310
|
+
function k(e, t, o) {
|
|
311
|
+
const s = F(t, o).getContext("2d");
|
|
312
|
+
s.drawImage(e, 0, 0);
|
|
313
|
+
const c = s.getImageData(0, 0, t, o).data;
|
|
314
|
+
return new Uint8Array(c.buffer, c.byteOffset, c.byteLength);
|
|
314
315
|
}
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
type: "object",
|
|
318
|
-
required: ["x", "y", "width", "height"],
|
|
319
|
-
additionalProperties: !1,
|
|
320
|
-
properties: {
|
|
321
|
-
x: { type: "number" },
|
|
322
|
-
y: { type: "number" },
|
|
323
|
-
width: { type: "number", minimum: 0 },
|
|
324
|
-
height: { type: "number", minimum: 0 }
|
|
325
|
-
}
|
|
326
|
-
},
|
|
327
|
-
Point: {
|
|
328
|
-
type: "object",
|
|
329
|
-
required: ["x", "y"],
|
|
330
|
-
additionalProperties: !1,
|
|
331
|
-
properties: {
|
|
332
|
-
x: { type: "number" },
|
|
333
|
-
y: { type: "number" }
|
|
334
|
-
}
|
|
335
|
-
},
|
|
336
|
-
Intent: {
|
|
337
|
-
type: "string",
|
|
338
|
-
enum: ["info", "warning", "error", "success", "neutral"]
|
|
339
|
-
},
|
|
340
|
-
AnnotationStyle: {
|
|
341
|
-
type: "object",
|
|
342
|
-
additionalProperties: !1,
|
|
343
|
-
properties: {
|
|
344
|
-
intent: { $ref: "#/$defs/Intent" },
|
|
345
|
-
stroke: { type: "string" },
|
|
346
|
-
strokeWidth: { type: "number", minimum: 0 },
|
|
347
|
-
fill: { type: "string" },
|
|
348
|
-
color: { type: "string" }
|
|
349
|
-
}
|
|
350
|
-
},
|
|
316
|
+
const I = {
|
|
317
|
+
...te,
|
|
351
318
|
Locator: { type: "string", minLength: 1 }
|
|
352
|
-
},
|
|
353
|
-
type: "object",
|
|
354
|
-
required: ["type", "bbox"],
|
|
355
|
-
additionalProperties: !1,
|
|
356
|
-
properties: {
|
|
357
|
-
type: { const: "rect" },
|
|
358
|
-
bbox: { $ref: "#/$defs/BBox" },
|
|
359
|
-
intent: { $ref: "#/$defs/Intent" },
|
|
360
|
-
stroke: { type: "string" },
|
|
361
|
-
strokeWidth: { type: "number", minimum: 0 },
|
|
362
|
-
fill: { type: "string" },
|
|
363
|
-
color: { type: "string" }
|
|
364
|
-
}
|
|
365
|
-
}, At = {
|
|
366
|
-
type: "object",
|
|
367
|
-
required: ["type", "center", "radius"],
|
|
368
|
-
additionalProperties: !1,
|
|
369
|
-
properties: {
|
|
370
|
-
type: { const: "circle" },
|
|
371
|
-
center: { $ref: "#/$defs/Point" },
|
|
372
|
-
radius: { type: "number", minimum: 0 },
|
|
373
|
-
intent: { $ref: "#/$defs/Intent" },
|
|
374
|
-
stroke: { type: "string" },
|
|
375
|
-
strokeWidth: { type: "number", minimum: 0 },
|
|
376
|
-
fill: { type: "string" },
|
|
377
|
-
color: { type: "string" }
|
|
378
|
-
}
|
|
379
|
-
}, Rt = {
|
|
380
|
-
type: "object",
|
|
381
|
-
required: ["type", "from", "to"],
|
|
382
|
-
additionalProperties: !1,
|
|
383
|
-
properties: {
|
|
384
|
-
type: { const: "arrow" },
|
|
385
|
-
from: { $ref: "#/$defs/Point" },
|
|
386
|
-
to: { $ref: "#/$defs/Point" },
|
|
387
|
-
intent: { $ref: "#/$defs/Intent" },
|
|
388
|
-
stroke: { type: "string" },
|
|
389
|
-
strokeWidth: { type: "number", minimum: 0 },
|
|
390
|
-
color: { type: "string" }
|
|
391
|
-
}
|
|
392
|
-
}, Tt = {
|
|
393
|
-
type: "object",
|
|
394
|
-
required: ["type", "at", "content"],
|
|
395
|
-
additionalProperties: !1,
|
|
396
|
-
properties: {
|
|
397
|
-
type: { const: "text" },
|
|
398
|
-
at: { $ref: "#/$defs/Point" },
|
|
399
|
-
content: { type: "string" },
|
|
400
|
-
fontSize: { type: "number", minimum: 0 },
|
|
401
|
-
anchor: { type: "string", enum: ["start", "middle", "end"] },
|
|
402
|
-
intent: { $ref: "#/$defs/Intent" },
|
|
403
|
-
color: { type: "string" }
|
|
404
|
-
}
|
|
405
|
-
}, kt = {
|
|
406
|
-
type: "object",
|
|
407
|
-
required: ["type", "at", "targetBbox", "content"],
|
|
408
|
-
additionalProperties: !1,
|
|
409
|
-
properties: {
|
|
410
|
-
type: { const: "callout" },
|
|
411
|
-
at: { $ref: "#/$defs/Point" },
|
|
412
|
-
targetBbox: { $ref: "#/$defs/BBox" },
|
|
413
|
-
content: { type: "string" },
|
|
414
|
-
intent: { $ref: "#/$defs/Intent" },
|
|
415
|
-
stroke: { type: "string" },
|
|
416
|
-
color: { type: "string" }
|
|
417
|
-
}
|
|
418
|
-
}, z = {
|
|
419
|
-
type: "object",
|
|
420
|
-
required: ["type", "svgFragment"],
|
|
421
|
-
additionalProperties: !1,
|
|
422
|
-
properties: {
|
|
423
|
-
type: { const: "raw" },
|
|
424
|
-
svgFragment: { type: "string" }
|
|
425
|
-
}
|
|
426
|
-
}, It = {
|
|
427
|
-
oneOf: [St, At, Rt, Tt, kt, z]
|
|
428
|
-
}, _t = {
|
|
319
|
+
}, Le = {
|
|
429
320
|
type: "object",
|
|
430
321
|
required: ["type"],
|
|
431
322
|
additionalProperties: !1,
|
|
@@ -440,7 +331,7 @@ const R = {
|
|
|
440
331
|
color: { type: "string" }
|
|
441
332
|
},
|
|
442
333
|
anyOf: [{ required: ["bbox"] }, { required: ["locator"] }]
|
|
443
|
-
},
|
|
334
|
+
}, Ae = {
|
|
444
335
|
type: "object",
|
|
445
336
|
required: ["type"],
|
|
446
337
|
additionalProperties: !1,
|
|
@@ -456,7 +347,7 @@ const R = {
|
|
|
456
347
|
color: { type: "string" }
|
|
457
348
|
},
|
|
458
349
|
anyOf: [{ required: ["center", "radius"] }, { required: ["locator"] }]
|
|
459
|
-
},
|
|
350
|
+
}, Te = {
|
|
460
351
|
type: "object",
|
|
461
352
|
required: ["type"],
|
|
462
353
|
additionalProperties: !1,
|
|
@@ -475,7 +366,7 @@ const R = {
|
|
|
475
366
|
{ anyOf: [{ required: ["from"] }, { required: ["fromLocator"] }] },
|
|
476
367
|
{ anyOf: [{ required: ["to"] }, { required: ["toLocator"] }] }
|
|
477
368
|
]
|
|
478
|
-
},
|
|
369
|
+
}, Re = {
|
|
479
370
|
type: "object",
|
|
480
371
|
required: ["type", "content"],
|
|
481
372
|
additionalProperties: !1,
|
|
@@ -490,7 +381,7 @@ const R = {
|
|
|
490
381
|
color: { type: "string" }
|
|
491
382
|
},
|
|
492
383
|
anyOf: [{ required: ["at"] }, { required: ["locator"] }]
|
|
493
|
-
},
|
|
384
|
+
}, Ie = {
|
|
494
385
|
type: "object",
|
|
495
386
|
required: ["type", "content"],
|
|
496
387
|
additionalProperties: !1,
|
|
@@ -509,18 +400,48 @@ const R = {
|
|
|
509
400
|
{ anyOf: [{ required: ["at"] }, { required: ["atLocator"] }] },
|
|
510
401
|
{ anyOf: [{ required: ["targetBbox"] }, { required: ["targetLocator"] }] }
|
|
511
402
|
]
|
|
512
|
-
},
|
|
513
|
-
oneOf: [_t, Bt, Ft, Ct, Ut, z]
|
|
514
|
-
}, qt = {
|
|
403
|
+
}, _e = {
|
|
515
404
|
type: "object",
|
|
516
|
-
required: ["
|
|
405
|
+
required: ["type", "svgFragment"],
|
|
517
406
|
additionalProperties: !1,
|
|
518
407
|
properties: {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
color: { type: "string" }
|
|
408
|
+
type: { const: "raw" },
|
|
409
|
+
svgFragment: { type: "string" }
|
|
522
410
|
}
|
|
523
|
-
},
|
|
411
|
+
}, Ce = {
|
|
412
|
+
oneOf: [Le, Ae, Te, Re, Ie, _e]
|
|
413
|
+
}, P = {
|
|
414
|
+
type: "object",
|
|
415
|
+
additionalProperties: !1,
|
|
416
|
+
properties: {
|
|
417
|
+
format: {
|
|
418
|
+
type: "string",
|
|
419
|
+
enum: ["smart", "png", "jpeg"],
|
|
420
|
+
description: "Encoding strategy. `smart` (default) picks PNG-8 for UI-heavy content and PNG-32 / JPEG for photo-heavy content per `smartFallback`. `png` forces PNG-32 (lossless). `jpeg` forces JPEG at `jpegPercent` quality."
|
|
421
|
+
},
|
|
422
|
+
saveSizePreset: {
|
|
423
|
+
type: "string",
|
|
424
|
+
enum: ["light", "standard", "highQuality", "original"],
|
|
425
|
+
description: "Max-width cap. `light` = 1280px, `standard` = 1920px (default), `highQuality` = 2560px, `original` = no resize. Aspect-preserving; never upscales."
|
|
426
|
+
},
|
|
427
|
+
smartFallback: {
|
|
428
|
+
type: "string",
|
|
429
|
+
enum: ["png", "jpeg"],
|
|
430
|
+
description: "Fallback format used by smart mode when the image is photo-heavy. Default `png`."
|
|
431
|
+
},
|
|
432
|
+
smartColorThreshold: {
|
|
433
|
+
type: "integer",
|
|
434
|
+
minimum: 1,
|
|
435
|
+
description: "Unique-colour count above which smart mode treats the image as photo-heavy. Default 15000."
|
|
436
|
+
},
|
|
437
|
+
jpegPercent: {
|
|
438
|
+
type: "integer",
|
|
439
|
+
minimum: 60,
|
|
440
|
+
maximum: 100,
|
|
441
|
+
description: "JPEG quality 60–100. Default 92."
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}, Ue = {
|
|
524
445
|
type: "object",
|
|
525
446
|
additionalProperties: !1,
|
|
526
447
|
properties: {
|
|
@@ -530,237 +451,144 @@ const R = {
|
|
|
530
451
|
color: { type: "string" }
|
|
531
452
|
},
|
|
532
453
|
anyOf: [{ required: ["bbox"] }, { required: ["locator"] }]
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
function V(t, e, r = {}) {
|
|
539
|
-
const o = r.color ?? "red", n = r.strokeWidth ?? 2, s = `annot-mcp-arrow-${Dt()}`;
|
|
540
|
-
return `<defs><marker id="${s}" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 5 L 0 10 z" fill="${v(o)}"/></marker></defs><line x1="${t.x}" y1="${t.y}" x2="${e.x}" y2="${e.y}" stroke="${v(o)}" stroke-width="${n}" marker-end="url(#${s})"/>`;
|
|
541
|
-
}
|
|
542
|
-
function J(t, e, r = {}) {
|
|
543
|
-
const o = r.color ?? "red", n = r.fontSize ?? 14, s = r.anchor ?? "start";
|
|
544
|
-
return `<text x="${t.x}" y="${t.y}" fill="${v(o)}" font-size="${n}" text-anchor="${s}">` + Gt(e) + "</text>";
|
|
545
|
-
}
|
|
546
|
-
let k = 0;
|
|
547
|
-
function Dt() {
|
|
548
|
-
return k = k + 1 | 0, k;
|
|
549
|
-
}
|
|
550
|
-
function v(t) {
|
|
551
|
-
return t.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
552
|
-
}
|
|
553
|
-
function Gt(t) {
|
|
554
|
-
return t.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
555
|
-
}
|
|
556
|
-
const Mt = {
|
|
557
|
-
info: { stroke: "#3b82f6", fill: "rgba(59, 130, 246, 0.12)", text: "#1e40af" },
|
|
558
|
-
warning: { stroke: "#f59e0b", fill: "rgba(245, 158, 11, 0.12)", text: "#92400e" },
|
|
559
|
-
error: { stroke: "#ef4444", fill: "rgba(239, 68, 68, 0.12)", text: "#991b1b" },
|
|
560
|
-
success: { stroke: "#10b981", fill: "rgba(16, 185, 129, 0.12)", text: "#065f46" },
|
|
561
|
-
neutral: { stroke: "#6b7280", fill: "rgba(107, 114, 128, 0.12)", text: "#374151" }
|
|
562
|
-
}, jt = "error";
|
|
563
|
-
function E(t) {
|
|
564
|
-
const e = t.intent ?? jt, r = Mt[e];
|
|
565
|
-
return {
|
|
566
|
-
stroke: t.stroke ?? r.stroke,
|
|
567
|
-
fill: t.fill ?? "none",
|
|
568
|
-
text: t.color ?? r.text
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
function C(t) {
|
|
572
|
-
return t.map(Ht).join("");
|
|
573
|
-
}
|
|
574
|
-
function Ht(t) {
|
|
575
|
-
switch (t.type) {
|
|
576
|
-
case "rect":
|
|
577
|
-
return Xt(t);
|
|
578
|
-
case "circle":
|
|
579
|
-
return zt(t);
|
|
580
|
-
case "arrow":
|
|
581
|
-
return Yt(t);
|
|
582
|
-
case "text":
|
|
583
|
-
return Vt(t);
|
|
584
|
-
case "callout":
|
|
585
|
-
return Jt(t);
|
|
586
|
-
case "raw":
|
|
587
|
-
return Kt(t);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
function Xt(t) {
|
|
591
|
-
const e = E(t);
|
|
592
|
-
return Y(t.bbox, {
|
|
593
|
-
stroke: e.stroke,
|
|
594
|
-
strokeWidth: t.strokeWidth ?? 2,
|
|
595
|
-
fill: e.fill
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
function zt(t) {
|
|
599
|
-
const e = E(t), r = t.strokeWidth ?? 2;
|
|
600
|
-
return `<circle cx="${t.center.x}" cy="${t.center.y}" r="${t.radius}" fill="${W(e.fill)}" stroke="${W(e.stroke)}" stroke-width="${r}"/>`;
|
|
601
|
-
}
|
|
602
|
-
function Yt(t) {
|
|
603
|
-
const e = E(t);
|
|
604
|
-
return V(t.from, t.to, {
|
|
605
|
-
color: e.stroke,
|
|
606
|
-
strokeWidth: t.strokeWidth ?? 2
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
function Vt(t) {
|
|
610
|
-
const e = E(t);
|
|
611
|
-
return J(t.at, t.content, {
|
|
612
|
-
color: e.text,
|
|
613
|
-
fontSize: t.fontSize ?? 14,
|
|
614
|
-
anchor: t.anchor ?? "start"
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
function Jt(t) {
|
|
618
|
-
const e = E(t), r = t.targetBbox, o = Zt(r, t.at);
|
|
619
|
-
return Y(r, {
|
|
620
|
-
stroke: e.stroke,
|
|
621
|
-
strokeWidth: t.strokeWidth ?? 2,
|
|
622
|
-
fill: e.fill
|
|
623
|
-
}) + V(t.at, o, {
|
|
624
|
-
color: e.stroke,
|
|
625
|
-
strokeWidth: 2
|
|
626
|
-
}) + J(t.at, t.content, {
|
|
627
|
-
color: e.text,
|
|
628
|
-
fontSize: 14,
|
|
629
|
-
anchor: "start"
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
function Kt(t) {
|
|
633
|
-
return t.svgFragment;
|
|
634
|
-
}
|
|
635
|
-
function Zt(t, e) {
|
|
636
|
-
const r = q(e.x, t.x, t.x + t.width), o = q(e.y, t.y, t.y + t.height);
|
|
637
|
-
return { x: r, y: o };
|
|
638
|
-
}
|
|
639
|
-
function q(t, e, r) {
|
|
640
|
-
return Math.min(Math.max(t, e), r);
|
|
641
|
-
}
|
|
642
|
-
function W(t) {
|
|
643
|
-
return t.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
644
|
-
}
|
|
645
|
-
const D = [137, 80, 78, 71, 13, 10, 26, 10], P = 12, Qt = "IHDR", te = 16, ee = 20, G = 24;
|
|
646
|
-
class $ extends Error {
|
|
647
|
-
constructor(e) {
|
|
648
|
-
super(e), this.name = "InvalidPngError";
|
|
454
|
+
}, j = [137, 80, 78, 71, 13, 10, 26, 10], L = 12, Fe = "IHDR", Ne = 16, Be = 20, G = 24;
|
|
455
|
+
class x extends Error {
|
|
456
|
+
constructor(t) {
|
|
457
|
+
super(t), this.name = "InvalidPngError";
|
|
649
458
|
}
|
|
650
459
|
}
|
|
651
|
-
function
|
|
652
|
-
if (
|
|
653
|
-
throw new
|
|
654
|
-
`Expected at least ${G} bytes for a PNG header, got ${
|
|
460
|
+
function B(e) {
|
|
461
|
+
if (e.byteLength < G)
|
|
462
|
+
throw new x(
|
|
463
|
+
`Expected at least ${G} bytes for a PNG header, got ${e.byteLength}.`
|
|
655
464
|
);
|
|
656
|
-
for (let
|
|
657
|
-
if (
|
|
658
|
-
throw new
|
|
465
|
+
for (let c = 0; c < j.length; c++)
|
|
466
|
+
if (e[c] !== j[c])
|
|
467
|
+
throw new x(
|
|
659
468
|
"Input does not start with the PNG signature (89 50 4E 47 0D 0A 1A 0A). Confirm the input is a PNG — JPEG / WebP / other formats are not supported in v1."
|
|
660
469
|
);
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
470
|
+
const t = new DataView(e.buffer, e.byteOffset, e.byteLength), o = String.fromCharCode(
|
|
471
|
+
e[L],
|
|
472
|
+
e[L + 1],
|
|
473
|
+
e[L + 2],
|
|
474
|
+
e[L + 3]
|
|
666
475
|
);
|
|
667
|
-
if (
|
|
668
|
-
throw new
|
|
669
|
-
`Expected IHDR chunk after PNG signature, found "${
|
|
476
|
+
if (o !== Fe)
|
|
477
|
+
throw new x(
|
|
478
|
+
`Expected IHDR chunk after PNG signature, found "${o}". The file may be a corrupt or non-standard PNG.`
|
|
670
479
|
);
|
|
671
|
-
const
|
|
672
|
-
if (
|
|
673
|
-
throw new
|
|
674
|
-
return { width:
|
|
675
|
-
}
|
|
676
|
-
class
|
|
677
|
-
constructor(
|
|
678
|
-
super(
|
|
480
|
+
const n = t.getUint32(Ne, !1), s = t.getUint32(Be, !1);
|
|
481
|
+
if (n === 0 || s === 0)
|
|
482
|
+
throw new x(`PNG dimensions ${n}×${s} include a zero axis.`);
|
|
483
|
+
return { width: n, height: s };
|
|
484
|
+
}
|
|
485
|
+
class w extends Error {
|
|
486
|
+
constructor(t) {
|
|
487
|
+
super(t), this.name = "InvalidImageInputError";
|
|
679
488
|
}
|
|
680
489
|
}
|
|
681
|
-
const
|
|
682
|
-
async function
|
|
683
|
-
if (typeof
|
|
684
|
-
throw new
|
|
490
|
+
const De = "data:", qe = /^data:image\/png;base64,/i;
|
|
491
|
+
async function R(e) {
|
|
492
|
+
if (typeof e != "string" || e.length === 0)
|
|
493
|
+
throw new w(
|
|
685
494
|
"`image` must be a non-empty string (data URL or absolute filesystem path)."
|
|
686
495
|
);
|
|
687
|
-
if (
|
|
688
|
-
return
|
|
689
|
-
if (!
|
|
690
|
-
throw new
|
|
691
|
-
`Filesystem path "${
|
|
496
|
+
if (e.startsWith(De))
|
|
497
|
+
return ke(e);
|
|
498
|
+
if (!b(e))
|
|
499
|
+
throw new w(
|
|
500
|
+
`Filesystem path "${e}" is not absolute. Pass an absolute path so resolution doesn't depend on the MCP server's working directory.`
|
|
692
501
|
);
|
|
693
|
-
return
|
|
502
|
+
return je(e);
|
|
694
503
|
}
|
|
695
|
-
function
|
|
696
|
-
if (!
|
|
697
|
-
throw new
|
|
504
|
+
function ke(e) {
|
|
505
|
+
if (!qe.test(e))
|
|
506
|
+
throw new w(
|
|
698
507
|
"Only `data:image/png;base64,...` URLs are supported. JPEG / WebP / SVG data URLs are not accepted in v1."
|
|
699
508
|
);
|
|
700
|
-
const
|
|
701
|
-
return { bytes:
|
|
509
|
+
const t = e.slice(e.indexOf(",") + 1), o = Ge(t), n = z(o);
|
|
510
|
+
return { bytes: o, dataUrl: e, dimensions: n };
|
|
702
511
|
}
|
|
703
|
-
async function
|
|
704
|
-
let
|
|
512
|
+
async function je(e) {
|
|
513
|
+
let t;
|
|
705
514
|
try {
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
} catch (
|
|
709
|
-
const
|
|
710
|
-
throw new
|
|
515
|
+
const s = await ce(e);
|
|
516
|
+
t = new Uint8Array(s.buffer, s.byteOffset, s.byteLength);
|
|
517
|
+
} catch (s) {
|
|
518
|
+
const c = s instanceof Error ? s.message : String(s);
|
|
519
|
+
throw new w(`Failed to read image at "${e}": ${c}`);
|
|
711
520
|
}
|
|
712
|
-
const
|
|
713
|
-
return { bytes:
|
|
521
|
+
const o = z(t), n = `data:image/png;base64,${Me(t)}`;
|
|
522
|
+
return { bytes: t, dataUrl: n, dimensions: o };
|
|
714
523
|
}
|
|
715
|
-
function
|
|
524
|
+
function z(e) {
|
|
716
525
|
try {
|
|
717
|
-
return
|
|
718
|
-
} catch (
|
|
719
|
-
throw
|
|
526
|
+
return B(e);
|
|
527
|
+
} catch (t) {
|
|
528
|
+
throw t instanceof x ? new w(t.message) : t;
|
|
720
529
|
}
|
|
721
530
|
}
|
|
722
|
-
function
|
|
723
|
-
const
|
|
724
|
-
return new Uint8Array(
|
|
725
|
-
}
|
|
726
|
-
function
|
|
727
|
-
return Buffer.from(
|
|
728
|
-
}
|
|
729
|
-
const
|
|
730
|
-
async function
|
|
731
|
-
if (
|
|
732
|
-
return
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
for (const
|
|
736
|
-
if (
|
|
737
|
-
switch (
|
|
531
|
+
function Ge(e) {
|
|
532
|
+
const t = Buffer.from(e, "base64");
|
|
533
|
+
return new Uint8Array(t.buffer, t.byteOffset, t.byteLength);
|
|
534
|
+
}
|
|
535
|
+
function Me(e) {
|
|
536
|
+
return Buffer.from(e.buffer, e.byteOffset, e.byteLength).toString("base64");
|
|
537
|
+
}
|
|
538
|
+
const He = "solid", We = "#000000", M = 16, Xe = 12;
|
|
539
|
+
async function Y(e, t) {
|
|
540
|
+
if (t.length === 0)
|
|
541
|
+
return e;
|
|
542
|
+
const o = await C(Buffer.from(e)), n = F(o.width, o.height), s = n.getContext("2d");
|
|
543
|
+
s.drawImage(o, 0, 0);
|
|
544
|
+
for (const r of t) {
|
|
545
|
+
if (r.bbox.width <= 0 || r.bbox.height <= 0) continue;
|
|
546
|
+
switch (r.style ?? He) {
|
|
738
547
|
case "solid":
|
|
739
|
-
|
|
548
|
+
ze(s, r.bbox, r.color ?? We);
|
|
740
549
|
break;
|
|
741
550
|
case "mosaic":
|
|
742
|
-
|
|
551
|
+
Ye(s, o, r.bbox);
|
|
743
552
|
break;
|
|
744
553
|
case "blur":
|
|
745
|
-
|
|
554
|
+
Ve(s, o, r.bbox);
|
|
746
555
|
break;
|
|
747
556
|
}
|
|
748
557
|
}
|
|
749
|
-
const
|
|
750
|
-
return new Uint8Array(
|
|
558
|
+
const c = n.toBuffer("image/png");
|
|
559
|
+
return new Uint8Array(c.buffer, c.byteOffset, c.byteLength);
|
|
751
560
|
}
|
|
752
|
-
function
|
|
753
|
-
|
|
561
|
+
function ze(e, t, o) {
|
|
562
|
+
e.save(), e.fillStyle = o, e.fillRect(t.x, t.y, t.width, t.height), e.restore();
|
|
754
563
|
}
|
|
755
|
-
function
|
|
756
|
-
const
|
|
757
|
-
|
|
564
|
+
function Ye(e, t, o) {
|
|
565
|
+
const n = Math.max(1, Math.round(o.width / M)), s = Math.max(1, Math.round(o.height / M)), c = F(n, s), r = c.getContext("2d");
|
|
566
|
+
r.imageSmoothingEnabled = !1, r.drawImage(t, o.x, o.y, o.width, o.height, 0, 0, n, s), e.save(), e.imageSmoothingEnabled = !1, e.drawImage(c, 0, 0, n, s, o.x, o.y, o.width, o.height), e.restore();
|
|
758
567
|
}
|
|
759
|
-
function
|
|
760
|
-
|
|
568
|
+
function Ve(e, t, o) {
|
|
569
|
+
e.save(), e.beginPath(), e.rect(o.x, o.y, o.width, o.height), e.clip(), e.filter = `blur(${Xe}px)`, e.drawImage(t, 0, 0), e.restore();
|
|
761
570
|
}
|
|
762
|
-
|
|
763
|
-
|
|
571
|
+
async function S(e, t, o) {
|
|
572
|
+
if (!o || Object.keys(o).length === 0)
|
|
573
|
+
return {
|
|
574
|
+
bytes: e,
|
|
575
|
+
mimeType: "image/png",
|
|
576
|
+
chosen: "png",
|
|
577
|
+
width: t.width,
|
|
578
|
+
height: t.height
|
|
579
|
+
};
|
|
580
|
+
const n = { ...oe, ...o }, s = await re(e, n);
|
|
581
|
+
return {
|
|
582
|
+
bytes: s.bytes,
|
|
583
|
+
mimeType: s.chosen === "jpeg" ? "image/jpeg" : "image/png",
|
|
584
|
+
chosen: s.chosen,
|
|
585
|
+
width: s.width,
|
|
586
|
+
height: s.height,
|
|
587
|
+
...s.reason ? { reason: s.reason } : {}
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
const V = "annot_annotate_screenshot", Je = {
|
|
591
|
+
name: V,
|
|
764
592
|
description: "Overlay annotations (rectangles, circles, arrows, callouts, text) on a PNG screenshot. Annotation positions are specified as bounding boxes or points; pair with `annot_annotate_url` if you want locator-driven positioning. Returns the annotated PNG inline unless `output` is specified, in which case the PNG is written to that absolute path.",
|
|
765
593
|
inputSchema: {
|
|
766
594
|
type: "object",
|
|
@@ -779,76 +607,92 @@ const tt = "annot_annotate_screenshot", pe = {
|
|
|
779
607
|
output: {
|
|
780
608
|
type: "string",
|
|
781
609
|
description: "Optional absolute filesystem path. When set, the annotated PNG is written here and the tool returns a text confirmation; otherwise the PNG bytes are returned inline."
|
|
610
|
+
},
|
|
611
|
+
encode: {
|
|
612
|
+
$ref: "#/$defs/EncodeOptions",
|
|
613
|
+
description: 'Optional encoder settings. When set, the output PNG-32 is decoded and re-encoded per `format` / `saveSizePreset` for size savings — `format: "smart"` + `saveSizePreset: "standard"` gives ~50–80% byte reduction typical for UI screenshots.'
|
|
782
614
|
}
|
|
783
615
|
},
|
|
784
616
|
$defs: {
|
|
785
|
-
...
|
|
786
|
-
BboxAnnotation:
|
|
617
|
+
...I,
|
|
618
|
+
BboxAnnotation: ne,
|
|
619
|
+
EncodeOptions: P
|
|
787
620
|
}
|
|
788
621
|
}
|
|
789
622
|
};
|
|
790
|
-
async function
|
|
623
|
+
async function Qe(e, t) {
|
|
791
624
|
try {
|
|
792
|
-
const
|
|
793
|
-
originalDataUrl:
|
|
794
|
-
annotationsSvg:
|
|
795
|
-
width:
|
|
796
|
-
height:
|
|
797
|
-
});
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
625
|
+
const o = Ke(e), n = await R(o.image), s = U(o.annotations), c = t.annotator.toPng({
|
|
626
|
+
originalDataUrl: n.dataUrl,
|
|
627
|
+
annotationsSvg: s,
|
|
628
|
+
width: n.dimensions.width,
|
|
629
|
+
height: n.dimensions.height
|
|
630
|
+
}), r = await S(c, n.dimensions, o.encode);
|
|
631
|
+
if (o.output) {
|
|
632
|
+
await O(o.output, r.bytes);
|
|
633
|
+
const a = r.reason ? `, reason: ${r.reason}` : "";
|
|
634
|
+
return {
|
|
635
|
+
content: [
|
|
636
|
+
{
|
|
637
|
+
type: "text",
|
|
638
|
+
text: `Wrote ${r.bytes.byteLength}-byte ${r.chosen.toUpperCase()} to ${o.output} (${r.width}×${r.height}, ${o.annotations.length} annotation${o.annotations.length === 1 ? "" : "s"}${a}).`
|
|
639
|
+
}
|
|
640
|
+
]
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
806
644
|
content: [
|
|
807
645
|
{
|
|
808
646
|
type: "image",
|
|
809
|
-
data:
|
|
810
|
-
mimeType:
|
|
647
|
+
data: et(r.bytes),
|
|
648
|
+
mimeType: r.mimeType
|
|
811
649
|
}
|
|
812
650
|
]
|
|
813
651
|
};
|
|
814
|
-
} catch (
|
|
815
|
-
return
|
|
652
|
+
} catch (o) {
|
|
653
|
+
return Ze(o);
|
|
816
654
|
}
|
|
817
655
|
}
|
|
818
|
-
function
|
|
819
|
-
if (typeof
|
|
820
|
-
throw new
|
|
821
|
-
if (!Array.isArray(
|
|
822
|
-
throw new
|
|
823
|
-
const
|
|
824
|
-
let
|
|
825
|
-
if (
|
|
826
|
-
if (typeof
|
|
827
|
-
throw new
|
|
828
|
-
if (!
|
|
829
|
-
throw new
|
|
830
|
-
`\`output\` path "${
|
|
656
|
+
function Ke(e) {
|
|
657
|
+
if (typeof e.image != "string")
|
|
658
|
+
throw new w("`image` is required and must be a string.");
|
|
659
|
+
if (!Array.isArray(e.annotations))
|
|
660
|
+
throw new A("`annotations` is required and must be an array.");
|
|
661
|
+
const t = e.annotations;
|
|
662
|
+
let o;
|
|
663
|
+
if (e.output !== void 0) {
|
|
664
|
+
if (typeof e.output != "string")
|
|
665
|
+
throw new A("`output` must be a string when provided.");
|
|
666
|
+
if (!b(e.output))
|
|
667
|
+
throw new A(
|
|
668
|
+
`\`output\` path "${e.output}" must be absolute so the file lands at a predictable location.`
|
|
831
669
|
);
|
|
832
|
-
|
|
670
|
+
o = e.output;
|
|
671
|
+
}
|
|
672
|
+
let n;
|
|
673
|
+
if (e.encode !== void 0) {
|
|
674
|
+
if (typeof e.encode != "object" || e.encode === null)
|
|
675
|
+
throw new A("`encode` must be an object when provided.");
|
|
676
|
+
n = e.encode;
|
|
833
677
|
}
|
|
834
|
-
return { image:
|
|
678
|
+
return { image: e.image, annotations: t, output: o, encode: n };
|
|
835
679
|
}
|
|
836
|
-
class
|
|
837
|
-
constructor(
|
|
838
|
-
super(
|
|
680
|
+
class A extends Error {
|
|
681
|
+
constructor(t) {
|
|
682
|
+
super(t), this.name = "InvalidToolInputError";
|
|
839
683
|
}
|
|
840
684
|
}
|
|
841
|
-
function
|
|
685
|
+
function Ze(e) {
|
|
842
686
|
return {
|
|
843
|
-
content: [{ type: "text", text:
|
|
687
|
+
content: [{ type: "text", text: e instanceof Error ? `${e.name}: ${e.message}` : `Unknown error: ${String(e)}` }],
|
|
844
688
|
isError: !0
|
|
845
689
|
};
|
|
846
690
|
}
|
|
847
|
-
function
|
|
848
|
-
return Buffer.from(
|
|
691
|
+
function et(e) {
|
|
692
|
+
return Buffer.from(e.buffer, e.byteOffset, e.byteLength).toString("base64");
|
|
849
693
|
}
|
|
850
|
-
const
|
|
851
|
-
name:
|
|
694
|
+
const J = "annot_annotate_url", tt = {
|
|
695
|
+
name: J,
|
|
852
696
|
description: 'Open a URL in a headless browser, capture a screenshot, and overlay annotations whose positions are specified as Playwright locator strings (`button:has-text("Submit")`, `[data-testid="email"]`, `role=button[name="Sign in"]`) or bounding boxes. Returns the annotated PNG inline unless `output` is specified.',
|
|
853
697
|
inputSchema: {
|
|
854
698
|
type: "object",
|
|
@@ -888,104 +732,121 @@ const et = "annot_annotate_url", be = {
|
|
|
888
732
|
output: {
|
|
889
733
|
type: "string",
|
|
890
734
|
description: "Optional absolute filesystem path. When set, the annotated PNG is written here and the tool returns a text confirmation."
|
|
735
|
+
},
|
|
736
|
+
encode: {
|
|
737
|
+
$ref: "#/$defs/EncodeOptions",
|
|
738
|
+
description: "Optional encoder settings (`format` / `saveSizePreset` / `jpegPercent`). Set to shrink the output for ingestion into issue trackers / docs."
|
|
891
739
|
}
|
|
892
740
|
},
|
|
893
741
|
$defs: {
|
|
894
|
-
...
|
|
895
|
-
LocatorAnnotation:
|
|
742
|
+
...I,
|
|
743
|
+
LocatorAnnotation: Ce,
|
|
744
|
+
EncodeOptions: P
|
|
896
745
|
}
|
|
897
746
|
}
|
|
898
747
|
};
|
|
899
|
-
class
|
|
900
|
-
constructor(
|
|
901
|
-
super(
|
|
748
|
+
class y extends Error {
|
|
749
|
+
constructor(t) {
|
|
750
|
+
super(t), this.name = "InvalidAnnotateUrlInputError";
|
|
902
751
|
}
|
|
903
752
|
}
|
|
904
|
-
async function
|
|
753
|
+
async function ot(e, t) {
|
|
905
754
|
try {
|
|
906
|
-
const
|
|
907
|
-
url:
|
|
908
|
-
viewport:
|
|
909
|
-
fullPage:
|
|
910
|
-
waitFor:
|
|
755
|
+
const o = rt(e), n = await W(t.pool, {
|
|
756
|
+
url: o.url,
|
|
757
|
+
viewport: o.viewport,
|
|
758
|
+
fullPage: o.fullPage,
|
|
759
|
+
waitFor: o.waitFor
|
|
911
760
|
});
|
|
912
|
-
let
|
|
761
|
+
let s;
|
|
913
762
|
try {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
763
|
+
s = await be(
|
|
764
|
+
n.handle.page,
|
|
765
|
+
o.annotations
|
|
917
766
|
);
|
|
918
767
|
} finally {
|
|
919
|
-
await
|
|
768
|
+
await n.handle.close();
|
|
920
769
|
}
|
|
921
|
-
const
|
|
922
|
-
originalDataUrl: `data:image/png;base64,${
|
|
923
|
-
annotationsSvg:
|
|
924
|
-
width:
|
|
925
|
-
height:
|
|
926
|
-
});
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
770
|
+
const c = B(n.pngBytes), r = U(s), a = t.annotator.toPng({
|
|
771
|
+
originalDataUrl: `data:image/png;base64,${H(n.pngBytes)}`,
|
|
772
|
+
annotationsSvg: r,
|
|
773
|
+
width: c.width,
|
|
774
|
+
height: c.height
|
|
775
|
+
}), i = await S(a, c, o.encode);
|
|
776
|
+
if (o.output) {
|
|
777
|
+
await O(o.output, i.bytes);
|
|
778
|
+
const d = i.reason ? `, reason: ${i.reason}` : "";
|
|
779
|
+
return {
|
|
780
|
+
content: [
|
|
781
|
+
{
|
|
782
|
+
type: "text",
|
|
783
|
+
text: `Wrote ${i.bytes.byteLength}-byte ${i.chosen.toUpperCase()} to ${o.output} (${i.width}×${i.height}, ${o.annotations.length} annotation${o.annotations.length === 1 ? "" : "s"}, captured from ${o.url}${d}).`
|
|
784
|
+
}
|
|
785
|
+
]
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
return {
|
|
935
789
|
content: [
|
|
936
790
|
{
|
|
937
791
|
type: "image",
|
|
938
|
-
data:
|
|
939
|
-
mimeType:
|
|
792
|
+
data: H(i.bytes),
|
|
793
|
+
mimeType: i.mimeType
|
|
940
794
|
}
|
|
941
795
|
]
|
|
942
796
|
};
|
|
943
|
-
} catch (
|
|
944
|
-
return
|
|
797
|
+
} catch (o) {
|
|
798
|
+
return nt(o);
|
|
945
799
|
}
|
|
946
800
|
}
|
|
947
|
-
function
|
|
948
|
-
if (typeof
|
|
949
|
-
throw new
|
|
950
|
-
if (!Array.isArray(
|
|
951
|
-
throw new
|
|
952
|
-
const
|
|
953
|
-
let
|
|
954
|
-
if (
|
|
955
|
-
if (typeof
|
|
956
|
-
throw new
|
|
957
|
-
const
|
|
958
|
-
|
|
801
|
+
function rt(e) {
|
|
802
|
+
if (typeof e.url != "string" || e.url.length === 0)
|
|
803
|
+
throw new y("`url` is required and must be a non-empty string.");
|
|
804
|
+
if (!Array.isArray(e.annotations))
|
|
805
|
+
throw new y("`annotations` is required and must be an array.");
|
|
806
|
+
const t = e.annotations;
|
|
807
|
+
let o;
|
|
808
|
+
if (e.viewport !== void 0) {
|
|
809
|
+
if (typeof e.viewport != "object" || e.viewport === null)
|
|
810
|
+
throw new y("`viewport` must be an object when provided.");
|
|
811
|
+
const a = e.viewport, i = typeof a.width == "number" ? a.width : 1280, d = typeof a.height == "number" ? a.height : 800, l = typeof a.deviceScaleFactor == "number" ? a.deviceScaleFactor : 1;
|
|
812
|
+
o = { width: i, height: d, deviceScaleFactor: l };
|
|
959
813
|
}
|
|
960
|
-
const
|
|
961
|
-
let
|
|
962
|
-
if (
|
|
963
|
-
if (typeof
|
|
964
|
-
throw new
|
|
965
|
-
if (!
|
|
966
|
-
throw new
|
|
967
|
-
|
|
814
|
+
const n = e.fullPage === !0, s = e.waitFor === "domcontentloaded" || e.waitFor === "networkidle" ? e.waitFor : "load";
|
|
815
|
+
let c;
|
|
816
|
+
if (e.output !== void 0) {
|
|
817
|
+
if (typeof e.output != "string")
|
|
818
|
+
throw new y("`output` must be a string when provided.");
|
|
819
|
+
if (!b(e.output))
|
|
820
|
+
throw new y(`\`output\` path "${e.output}" must be absolute.`);
|
|
821
|
+
c = e.output;
|
|
822
|
+
}
|
|
823
|
+
let r;
|
|
824
|
+
if (e.encode !== void 0) {
|
|
825
|
+
if (typeof e.encode != "object" || e.encode === null)
|
|
826
|
+
throw new y("`encode` must be an object when provided.");
|
|
827
|
+
r = e.encode;
|
|
968
828
|
}
|
|
969
829
|
return {
|
|
970
|
-
url:
|
|
971
|
-
annotations:
|
|
972
|
-
viewport:
|
|
973
|
-
fullPage:
|
|
974
|
-
waitFor:
|
|
975
|
-
output:
|
|
830
|
+
url: e.url,
|
|
831
|
+
annotations: t,
|
|
832
|
+
viewport: o,
|
|
833
|
+
fullPage: n,
|
|
834
|
+
waitFor: s,
|
|
835
|
+
output: c,
|
|
836
|
+
encode: r
|
|
976
837
|
};
|
|
977
838
|
}
|
|
978
|
-
function
|
|
839
|
+
function nt(e) {
|
|
979
840
|
return {
|
|
980
|
-
content: [{ type: "text", text:
|
|
841
|
+
content: [{ type: "text", text: e instanceof Error ? `${e.name}: ${e.message}` : `Unknown error: ${String(e)}` }],
|
|
981
842
|
isError: !0
|
|
982
843
|
};
|
|
983
844
|
}
|
|
984
|
-
function
|
|
985
|
-
return Buffer.from(
|
|
845
|
+
function H(e) {
|
|
846
|
+
return Buffer.from(e.buffer, e.byteOffset, e.byteLength).toString("base64");
|
|
986
847
|
}
|
|
987
|
-
const
|
|
988
|
-
name:
|
|
848
|
+
const Q = "annot_compare_screenshots", st = {
|
|
849
|
+
name: Q,
|
|
989
850
|
description: "Compare two screenshots (must have identical dimensions) and return a PNG of the `after` image with changed regions highlighted as red rectangles. Useful for visual-diff PR review.",
|
|
990
851
|
inputSchema: {
|
|
991
852
|
type: "object",
|
|
@@ -1012,69 +873,88 @@ const rt = "annot_compare_screenshots", Ee = {
|
|
|
1012
873
|
default: !1,
|
|
1013
874
|
description: "When true, append a text content block listing the changed-region bboxes alongside the annotated PNG."
|
|
1014
875
|
},
|
|
1015
|
-
output: { type: "string" }
|
|
876
|
+
output: { type: "string" },
|
|
877
|
+
encode: { $ref: "#/$defs/EncodeOptions" }
|
|
878
|
+
},
|
|
879
|
+
$defs: {
|
|
880
|
+
EncodeOptions: P
|
|
1016
881
|
}
|
|
1017
882
|
}
|
|
1018
883
|
};
|
|
1019
|
-
class
|
|
1020
|
-
constructor(
|
|
1021
|
-
super(
|
|
884
|
+
class $ extends Error {
|
|
885
|
+
constructor(t) {
|
|
886
|
+
super(t), this.name = "InvalidCompareInputError";
|
|
1022
887
|
}
|
|
1023
888
|
}
|
|
1024
|
-
async function
|
|
889
|
+
async function it(e, t) {
|
|
1025
890
|
try {
|
|
1026
|
-
if (typeof
|
|
1027
|
-
throw new
|
|
1028
|
-
if (typeof
|
|
1029
|
-
throw new
|
|
1030
|
-
const
|
|
1031
|
-
let
|
|
1032
|
-
if (
|
|
1033
|
-
if (typeof
|
|
1034
|
-
throw new
|
|
1035
|
-
if (!
|
|
1036
|
-
throw new
|
|
1037
|
-
|
|
891
|
+
if (typeof e.before != "string")
|
|
892
|
+
throw new $("`before` is required and must be a string.");
|
|
893
|
+
if (typeof e.after != "string")
|
|
894
|
+
throw new $("`after` is required and must be a string.");
|
|
895
|
+
const o = typeof e.threshold == "number" ? e.threshold : 0.1, n = e.includeChangeList === !0;
|
|
896
|
+
let s;
|
|
897
|
+
if (e.output !== void 0) {
|
|
898
|
+
if (typeof e.output != "string")
|
|
899
|
+
throw new $("`output` must be a string when provided.");
|
|
900
|
+
if (!b(e.output))
|
|
901
|
+
throw new $(`\`output\` path "${e.output}" must be absolute.`);
|
|
902
|
+
s = e.output;
|
|
1038
903
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
904
|
+
let c;
|
|
905
|
+
if (e.encode !== void 0) {
|
|
906
|
+
if (typeof e.encode != "object" || e.encode === null)
|
|
907
|
+
throw new $("`encode` must be an object when provided.");
|
|
908
|
+
c = e.encode;
|
|
909
|
+
}
|
|
910
|
+
const [r, a] = await Promise.all([
|
|
911
|
+
R(e.before),
|
|
912
|
+
R(e.after)
|
|
913
|
+
]), i = await Se(r.bytes, a.bytes, { threshold: o }), d = i.regions.map((h) => ({
|
|
1043
914
|
type: "rect",
|
|
1044
|
-
bbox:
|
|
915
|
+
bbox: h,
|
|
1045
916
|
intent: "warning"
|
|
1046
|
-
})), l =
|
|
1047
|
-
originalDataUrl:
|
|
917
|
+
})), l = U(d), v = t.annotator.toPng({
|
|
918
|
+
originalDataUrl: a.dataUrl,
|
|
1048
919
|
annotationsSvg: l,
|
|
1049
|
-
width:
|
|
1050
|
-
height:
|
|
1051
|
-
})
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
920
|
+
width: i.width,
|
|
921
|
+
height: i.height
|
|
922
|
+
}), u = await S(
|
|
923
|
+
v,
|
|
924
|
+
{ width: i.width, height: i.height },
|
|
925
|
+
c
|
|
926
|
+
);
|
|
927
|
+
if (s) {
|
|
928
|
+
await O(s, u.bytes);
|
|
929
|
+
const h = u.reason ? `, reason: ${u.reason}` : "";
|
|
930
|
+
return { content: [{ type: "text", text: `Wrote ${u.bytes.byteLength}-byte diff-annotated ${u.chosen.toUpperCase()} to ${s} (${u.width}×${u.height}, ${i.regions.length} changed region${i.regions.length === 1 ? "" : "s"}, ${i.mismatchedPixels} mismatched pixel${i.mismatchedPixels === 1 ? "" : "s"}${h}).` }] };
|
|
931
|
+
}
|
|
932
|
+
const _ = {
|
|
1055
933
|
type: "image",
|
|
1056
|
-
data: Buffer.from(
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
934
|
+
data: Buffer.from(
|
|
935
|
+
u.bytes.buffer,
|
|
936
|
+
u.bytes.byteOffset,
|
|
937
|
+
u.bytes.byteLength
|
|
938
|
+
).toString("base64"),
|
|
939
|
+
mimeType: u.mimeType
|
|
1060
940
|
};
|
|
1061
|
-
if (!
|
|
1062
|
-
return { content: [
|
|
1063
|
-
const
|
|
1064
|
-
` +
|
|
941
|
+
if (!n)
|
|
942
|
+
return { content: [_] };
|
|
943
|
+
const ee = `${i.regions.length} changed region${i.regions.length === 1 ? "" : "s"}, ${i.mismatchedPixels} mismatched pixel${i.mismatchedPixels === 1 ? "" : "s"}:
|
|
944
|
+
` + i.regions.map((h, D) => ` [${D + 1}] x=${h.x} y=${h.y} w=${h.width} h=${h.height}`).join(`
|
|
1065
945
|
`);
|
|
1066
946
|
return {
|
|
1067
|
-
content: [
|
|
947
|
+
content: [_, { type: "text", text: ee }]
|
|
1068
948
|
};
|
|
1069
|
-
} catch (
|
|
949
|
+
} catch (o) {
|
|
1070
950
|
return {
|
|
1071
|
-
content: [{ type: "text", text:
|
|
951
|
+
content: [{ type: "text", text: o instanceof Error ? `${o.name}: ${o.message}` : `Unknown error: ${String(o)}` }],
|
|
1072
952
|
isError: !0
|
|
1073
953
|
};
|
|
1074
954
|
}
|
|
1075
955
|
}
|
|
1076
|
-
const
|
|
1077
|
-
name:
|
|
956
|
+
const K = "annot_redact_screenshot", at = {
|
|
957
|
+
name: K,
|
|
1078
958
|
description: "Destructively burn redactions (solid / mosaic / blur) into a PNG screenshot. The original pixels under each region are irrecoverably replaced. Returns the redacted PNG inline unless `output` is specified.",
|
|
1079
959
|
inputSchema: {
|
|
1080
960
|
type: "object",
|
|
@@ -1089,64 +969,80 @@ const ot = "annot_redact_screenshot", Le = {
|
|
|
1089
969
|
type: "array",
|
|
1090
970
|
items: { $ref: "#/$defs/BboxRedactRegion" }
|
|
1091
971
|
},
|
|
1092
|
-
output: { type: "string" }
|
|
972
|
+
output: { type: "string" },
|
|
973
|
+
encode: {
|
|
974
|
+
$ref: "#/$defs/EncodeOptions",
|
|
975
|
+
description: "Optional encoder settings applied after the redactions are burned in. Useful when the redacted screenshot is destined for an issue tracker / docs and size matters."
|
|
976
|
+
}
|
|
1093
977
|
},
|
|
1094
978
|
$defs: {
|
|
1095
|
-
...
|
|
1096
|
-
BboxRedactRegion:
|
|
979
|
+
...I,
|
|
980
|
+
BboxRedactRegion: se,
|
|
981
|
+
EncodeOptions: P
|
|
1097
982
|
}
|
|
1098
983
|
}
|
|
1099
984
|
};
|
|
1100
|
-
class
|
|
1101
|
-
constructor(
|
|
1102
|
-
super(
|
|
985
|
+
class E extends Error {
|
|
986
|
+
constructor(t) {
|
|
987
|
+
super(t), this.name = "InvalidRedactInputError";
|
|
1103
988
|
}
|
|
1104
989
|
}
|
|
1105
|
-
async function
|
|
990
|
+
async function ct(e) {
|
|
1106
991
|
try {
|
|
1107
|
-
if (typeof
|
|
1108
|
-
throw new
|
|
1109
|
-
if (!Array.isArray(
|
|
1110
|
-
throw new
|
|
1111
|
-
const
|
|
1112
|
-
let
|
|
1113
|
-
if (
|
|
1114
|
-
if (typeof
|
|
1115
|
-
throw new
|
|
1116
|
-
if (!
|
|
1117
|
-
throw new
|
|
1118
|
-
|
|
992
|
+
if (typeof e.image != "string")
|
|
993
|
+
throw new E("`image` is required and must be a string.");
|
|
994
|
+
if (!Array.isArray(e.regions))
|
|
995
|
+
throw new E("`regions` is required and must be an array.");
|
|
996
|
+
const t = e.regions;
|
|
997
|
+
let o;
|
|
998
|
+
if (e.output !== void 0) {
|
|
999
|
+
if (typeof e.output != "string")
|
|
1000
|
+
throw new E("`output` must be a string when provided.");
|
|
1001
|
+
if (!b(e.output))
|
|
1002
|
+
throw new E(`\`output\` path "${e.output}" must be absolute.`);
|
|
1003
|
+
o = e.output;
|
|
1119
1004
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1005
|
+
let n;
|
|
1006
|
+
if (e.encode !== void 0) {
|
|
1007
|
+
if (typeof e.encode != "object" || e.encode === null)
|
|
1008
|
+
throw new E("`encode` must be an object when provided.");
|
|
1009
|
+
n = e.encode;
|
|
1010
|
+
}
|
|
1011
|
+
const s = await R(e.image), c = await Y(s.bytes, t), r = await S(c, s.dimensions, n);
|
|
1012
|
+
if (o) {
|
|
1013
|
+
await O(o, r.bytes);
|
|
1014
|
+
const a = r.reason ? `, reason: ${r.reason}` : "";
|
|
1015
|
+
return {
|
|
1016
|
+
content: [
|
|
1017
|
+
{
|
|
1018
|
+
type: "text",
|
|
1019
|
+
text: `Wrote ${r.bytes.byteLength}-byte redacted ${r.chosen.toUpperCase()} to ${o} (${r.width}×${r.height}, ${t.length} region${t.length === 1 ? "" : "s"}${a}).`
|
|
1020
|
+
}
|
|
1021
|
+
]
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
return {
|
|
1129
1025
|
content: [
|
|
1130
1026
|
{
|
|
1131
1027
|
type: "image",
|
|
1132
1028
|
data: Buffer.from(
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1029
|
+
r.bytes.buffer,
|
|
1030
|
+
r.bytes.byteOffset,
|
|
1031
|
+
r.bytes.byteLength
|
|
1136
1032
|
).toString("base64"),
|
|
1137
|
-
mimeType:
|
|
1033
|
+
mimeType: r.mimeType
|
|
1138
1034
|
}
|
|
1139
1035
|
]
|
|
1140
1036
|
};
|
|
1141
|
-
} catch (
|
|
1037
|
+
} catch (t) {
|
|
1142
1038
|
return {
|
|
1143
|
-
content: [{ type: "text", text:
|
|
1039
|
+
content: [{ type: "text", text: t instanceof Error ? `${t.name}: ${t.message}` : `Unknown error: ${String(t)}` }],
|
|
1144
1040
|
isError: !0
|
|
1145
1041
|
};
|
|
1146
1042
|
}
|
|
1147
1043
|
}
|
|
1148
|
-
const
|
|
1149
|
-
name:
|
|
1044
|
+
const Z = "annot_redact_url", dt = {
|
|
1045
|
+
name: Z,
|
|
1150
1046
|
description: "Open a URL in a headless browser, capture a screenshot, and destructively burn redactions over regions identified by locator strings or bboxes. Returns the redacted PNG inline unless `output` is specified.",
|
|
1151
1047
|
inputSchema: {
|
|
1152
1048
|
type: "object",
|
|
@@ -1173,183 +1069,199 @@ const nt = "annot_redact_url", Se = {
|
|
|
1173
1069
|
enum: ["load", "domcontentloaded", "networkidle"],
|
|
1174
1070
|
default: "load"
|
|
1175
1071
|
},
|
|
1176
|
-
output: { type: "string" }
|
|
1072
|
+
output: { type: "string" },
|
|
1073
|
+
encode: { $ref: "#/$defs/EncodeOptions" }
|
|
1177
1074
|
},
|
|
1178
1075
|
$defs: {
|
|
1179
|
-
...
|
|
1180
|
-
LocatorRedactRegion:
|
|
1076
|
+
...I,
|
|
1077
|
+
LocatorRedactRegion: Ue,
|
|
1078
|
+
EncodeOptions: P
|
|
1181
1079
|
}
|
|
1182
1080
|
}
|
|
1183
1081
|
};
|
|
1184
|
-
class
|
|
1185
|
-
constructor(
|
|
1186
|
-
super(
|
|
1082
|
+
class p extends Error {
|
|
1083
|
+
constructor(t) {
|
|
1084
|
+
super(t), this.name = "InvalidRedactUrlInputError";
|
|
1187
1085
|
}
|
|
1188
1086
|
}
|
|
1189
|
-
async function
|
|
1087
|
+
async function lt(e, t) {
|
|
1190
1088
|
try {
|
|
1191
|
-
const
|
|
1192
|
-
url:
|
|
1193
|
-
viewport:
|
|
1194
|
-
fullPage:
|
|
1195
|
-
waitFor:
|
|
1089
|
+
const o = ut(e), n = await W(t.pool, {
|
|
1090
|
+
url: o.url,
|
|
1091
|
+
viewport: o.viewport,
|
|
1092
|
+
fullPage: o.fullPage,
|
|
1093
|
+
waitFor: o.waitFor
|
|
1196
1094
|
});
|
|
1197
|
-
let
|
|
1095
|
+
let s;
|
|
1198
1096
|
try {
|
|
1199
|
-
|
|
1200
|
-
for (const i of
|
|
1201
|
-
const
|
|
1202
|
-
i.style !== void 0 && (
|
|
1097
|
+
s = [];
|
|
1098
|
+
for (const i of o.regions) {
|
|
1099
|
+
const l = { bbox: await ht(n.handle.page, i) };
|
|
1100
|
+
i.style !== void 0 && (l.style = i.style), i.color !== void 0 && (l.color = i.color), s.push(l);
|
|
1203
1101
|
}
|
|
1204
1102
|
} finally {
|
|
1205
|
-
await
|
|
1103
|
+
await n.handle.close();
|
|
1206
1104
|
}
|
|
1207
|
-
const
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1105
|
+
const c = await Y(n.pngBytes, s), r = B(n.pngBytes), a = await S(c, r, o.encode);
|
|
1106
|
+
if (o.output) {
|
|
1107
|
+
await O(o.output, a.bytes);
|
|
1108
|
+
const i = a.reason ? `, reason: ${a.reason}` : "";
|
|
1109
|
+
return {
|
|
1110
|
+
content: [
|
|
1111
|
+
{
|
|
1112
|
+
type: "text",
|
|
1113
|
+
text: `Wrote ${a.bytes.byteLength}-byte redacted ${a.chosen.toUpperCase()} to ${o.output} (${a.width}×${a.height}, ${o.regions.length} region${o.regions.length === 1 ? "" : "s"}, captured from ${o.url}${i}).`
|
|
1114
|
+
}
|
|
1115
|
+
]
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
return {
|
|
1216
1119
|
content: [
|
|
1217
1120
|
{
|
|
1218
1121
|
type: "image",
|
|
1219
1122
|
data: Buffer.from(
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1123
|
+
a.bytes.buffer,
|
|
1124
|
+
a.bytes.byteOffset,
|
|
1125
|
+
a.bytes.byteLength
|
|
1223
1126
|
).toString("base64"),
|
|
1224
|
-
mimeType:
|
|
1127
|
+
mimeType: a.mimeType
|
|
1225
1128
|
}
|
|
1226
1129
|
]
|
|
1227
1130
|
};
|
|
1228
|
-
} catch (
|
|
1131
|
+
} catch (o) {
|
|
1229
1132
|
return {
|
|
1230
|
-
content: [{ type: "text", text:
|
|
1133
|
+
content: [{ type: "text", text: o instanceof Error ? `${o.name}: ${o.message}` : `Unknown error: ${String(o)}` }],
|
|
1231
1134
|
isError: !0
|
|
1232
1135
|
};
|
|
1233
1136
|
}
|
|
1234
1137
|
}
|
|
1235
|
-
function
|
|
1236
|
-
if (typeof
|
|
1237
|
-
throw new
|
|
1238
|
-
if (!Array.isArray(
|
|
1239
|
-
throw new
|
|
1240
|
-
const
|
|
1241
|
-
let
|
|
1242
|
-
if (
|
|
1243
|
-
if (typeof
|
|
1244
|
-
throw new
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1138
|
+
function ut(e) {
|
|
1139
|
+
if (typeof e.url != "string" || e.url.length === 0)
|
|
1140
|
+
throw new p("`url` is required and must be a non-empty string.");
|
|
1141
|
+
if (!Array.isArray(e.regions))
|
|
1142
|
+
throw new p("`regions` is required and must be an array.");
|
|
1143
|
+
const t = e.regions;
|
|
1144
|
+
let o;
|
|
1145
|
+
if (e.viewport !== void 0) {
|
|
1146
|
+
if (typeof e.viewport != "object" || e.viewport === null)
|
|
1147
|
+
throw new p("`viewport` must be an object when provided.");
|
|
1148
|
+
const a = e.viewport, i = typeof a.width == "number" ? a.width : 1280, d = typeof a.height == "number" ? a.height : 800, l = typeof a.deviceScaleFactor == "number" ? a.deviceScaleFactor : 1;
|
|
1149
|
+
o = { width: i, height: d, deviceScaleFactor: l };
|
|
1247
1150
|
}
|
|
1248
|
-
const
|
|
1249
|
-
let
|
|
1250
|
-
if (
|
|
1251
|
-
if (typeof
|
|
1252
|
-
throw new
|
|
1253
|
-
if (!
|
|
1254
|
-
throw new
|
|
1255
|
-
|
|
1151
|
+
const n = e.fullPage === !0, s = e.waitFor === "domcontentloaded" || e.waitFor === "networkidle" ? e.waitFor : "load";
|
|
1152
|
+
let c;
|
|
1153
|
+
if (e.output !== void 0) {
|
|
1154
|
+
if (typeof e.output != "string")
|
|
1155
|
+
throw new p("`output` must be a string when provided.");
|
|
1156
|
+
if (!b(e.output))
|
|
1157
|
+
throw new p(`\`output\` path "${e.output}" must be absolute.`);
|
|
1158
|
+
c = e.output;
|
|
1256
1159
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1160
|
+
let r;
|
|
1161
|
+
if (e.encode !== void 0) {
|
|
1162
|
+
if (typeof e.encode != "object" || e.encode === null)
|
|
1163
|
+
throw new p("`encode` must be an object when provided.");
|
|
1164
|
+
r = e.encode;
|
|
1165
|
+
}
|
|
1166
|
+
return { url: e.url, regions: t, viewport: o, fullPage: n, waitFor: s, output: c, encode: r };
|
|
1167
|
+
}
|
|
1168
|
+
async function ht(e, t) {
|
|
1169
|
+
if (t.bbox !== void 0) return t.bbox;
|
|
1170
|
+
if (t.locator !== void 0) return f(e, t.locator);
|
|
1171
|
+
throw new p("redact region requires either `bbox` or `locator`.");
|
|
1172
|
+
}
|
|
1173
|
+
const ft = "annot-mcp", pt = "0.1.0", mt = [
|
|
1174
|
+
Je,
|
|
1175
|
+
tt,
|
|
1176
|
+
at,
|
|
1177
|
+
dt,
|
|
1178
|
+
st
|
|
1270
1179
|
];
|
|
1271
|
-
function
|
|
1272
|
-
const
|
|
1180
|
+
function gt(e = {}) {
|
|
1181
|
+
const t = new de(
|
|
1273
1182
|
{
|
|
1274
|
-
name:
|
|
1275
|
-
version:
|
|
1183
|
+
name: ft,
|
|
1184
|
+
version: e.version ?? pt
|
|
1276
1185
|
},
|
|
1277
1186
|
{
|
|
1278
1187
|
capabilities: {
|
|
1279
1188
|
tools: {}
|
|
1280
1189
|
}
|
|
1281
1190
|
}
|
|
1282
|
-
),
|
|
1283
|
-
return
|
|
1284
|
-
const { name:
|
|
1191
|
+
), o = e.annotator ?? ie(e.annotatorOptions ?? {}), n = e.pool ?? ye();
|
|
1192
|
+
return t.setRequestHandler(le, async () => ({ tools: mt })), t.setRequestHandler(ue, async (s) => {
|
|
1193
|
+
const { name: c, arguments: r } = s.params;
|
|
1285
1194
|
let a;
|
|
1286
|
-
switch (
|
|
1287
|
-
case
|
|
1288
|
-
a = await
|
|
1195
|
+
switch (c) {
|
|
1196
|
+
case V:
|
|
1197
|
+
a = await Qe(r ?? {}, { annotator: o });
|
|
1289
1198
|
break;
|
|
1290
|
-
case
|
|
1291
|
-
a = await
|
|
1199
|
+
case J:
|
|
1200
|
+
a = await ot(r ?? {}, { annotator: o, pool: n });
|
|
1292
1201
|
break;
|
|
1293
|
-
case
|
|
1294
|
-
a = await
|
|
1202
|
+
case K:
|
|
1203
|
+
a = await ct(r ?? {});
|
|
1295
1204
|
break;
|
|
1296
|
-
case
|
|
1297
|
-
a = await
|
|
1205
|
+
case Z:
|
|
1206
|
+
a = await lt(r ?? {}, { pool: n });
|
|
1298
1207
|
break;
|
|
1299
|
-
case
|
|
1300
|
-
a = await
|
|
1208
|
+
case Q:
|
|
1209
|
+
a = await it(r ?? {}, { annotator: o });
|
|
1301
1210
|
break;
|
|
1302
1211
|
default:
|
|
1303
1212
|
a = {
|
|
1304
|
-
content: [{ type: "text", text: `Unknown tool: ${
|
|
1213
|
+
content: [{ type: "text", text: `Unknown tool: ${c}` }],
|
|
1305
1214
|
isError: !0
|
|
1306
1215
|
};
|
|
1307
1216
|
}
|
|
1308
1217
|
return a;
|
|
1309
|
-
}),
|
|
1218
|
+
}), t;
|
|
1310
1219
|
}
|
|
1311
|
-
async function
|
|
1312
|
-
const
|
|
1313
|
-
await
|
|
1220
|
+
async function Pt() {
|
|
1221
|
+
const e = gt(), t = new he();
|
|
1222
|
+
await e.connect(t);
|
|
1314
1223
|
}
|
|
1315
1224
|
export {
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1225
|
+
V as ANNOTATE_SCREENSHOT_TOOL_NAME,
|
|
1226
|
+
J as ANNOTATE_URL_TOOL_NAME,
|
|
1227
|
+
At as BBOX_ANNOTATION_SCHEMA,
|
|
1228
|
+
Tt as BBOX_REDACT_REGION_SCHEMA,
|
|
1229
|
+
ge as BrowserPool,
|
|
1230
|
+
Q as COMPARE_SCREENSHOTS_TOOL_NAME,
|
|
1231
|
+
q as ChromiumUnavailableError,
|
|
1232
|
+
Pe as DimensionMismatchError,
|
|
1233
|
+
w as InvalidImageInputError,
|
|
1234
|
+
x as InvalidPngError,
|
|
1235
|
+
Ce as LOCATOR_ANNOTATION_SCHEMA,
|
|
1236
|
+
Ue as LOCATOR_REDACT_REGION_SCHEMA,
|
|
1237
|
+
N as LocatorResolutionError,
|
|
1238
|
+
K as REDACT_SCREENSHOT_TOOL_NAME,
|
|
1239
|
+
Z as REDACT_URL_TOOL_NAME,
|
|
1240
|
+
I as SHARED_DEFS,
|
|
1241
|
+
xe as aggregateDiffRegions,
|
|
1242
|
+
Je as annotateScreenshotTool,
|
|
1243
|
+
tt as annotateUrlTool,
|
|
1244
|
+
Rt as arrowBetween,
|
|
1245
|
+
It as bboxAnnotationsToSvg,
|
|
1246
|
+
Y as burnRedactions,
|
|
1247
|
+
W as capturePage,
|
|
1248
|
+
st as compareScreenshotsTool,
|
|
1249
|
+
ye as createChromiumPool,
|
|
1250
|
+
gt as createServer,
|
|
1251
|
+
Se as diffScreenshots,
|
|
1252
|
+
Qe as handleAnnotateScreenshot,
|
|
1253
|
+
ot as handleAnnotateUrl,
|
|
1254
|
+
it as handleCompareScreenshots,
|
|
1255
|
+
ct as handleRedactScreenshot,
|
|
1256
|
+
lt as handleRedactUrl,
|
|
1257
|
+
B as readPngDimensions,
|
|
1258
|
+
_t as rectForBoundingBox,
|
|
1259
|
+
at as redactScreenshotTool,
|
|
1260
|
+
dt as redactUrlTool,
|
|
1261
|
+
R as resolveImageInput,
|
|
1351
1262
|
f as resolveLocator,
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1263
|
+
we as resolveLocatorAnnotation,
|
|
1264
|
+
be as resolveLocatorAnnotations,
|
|
1265
|
+
Pt as runStdioServer,
|
|
1266
|
+
Ct as textAt
|
|
1355
1267
|
};
|