@aiscene/shared 8.0.3 → 8.0.4
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/es/agent-tools/agent-behavior-init-args.mjs +44 -0
- package/dist/es/agent-tools/base-tools.mjs +163 -0
- package/dist/es/agent-tools/chrome-path.mjs +50 -0
- package/dist/es/agent-tools/cli-report-session.mjs +78 -0
- package/dist/es/agent-tools/error-formatter.mjs +106 -0
- package/dist/es/agent-tools/index.mjs +9 -0
- package/dist/es/agent-tools/init-arg-utils.mjs +38 -0
- package/dist/es/agent-tools/observation-artifact.mjs +5 -0
- package/dist/es/agent-tools/observation-record.mjs +248 -0
- package/dist/es/agent-tools/tool-defaults.mjs +54 -0
- package/dist/es/agent-tools/tool-generator.mjs +529 -0
- package/dist/es/agent-tools/types.mjs +3 -0
- package/dist/es/agent-tools/user-prompt.mjs +66 -0
- package/dist/es/cli/interrupt.mjs +116 -0
- package/dist/es/cli/record-command.mjs +130 -0
- package/dist/es/cli/screenshot-file.mjs +24 -0
- package/dist/es/cli/verbose-ai-act.mjs +230 -0
- package/dist/es/cli/verbose-screenshot.mjs +134 -0
- package/dist/es/cli/verbose.mjs +404 -0
- package/dist/es/env/parse-model-config.mjs +1 -1
- package/dist/es/env/types.mjs +3 -1
- package/dist/es/img/info.mjs +45 -1
- package/dist/es/img/photon-loader.mjs +4 -0
- package/dist/es/recorder.mjs +249 -0
- package/dist/lib/agent-tools/agent-behavior-init-args.js +87 -0
- package/dist/lib/agent-tools/base-tools.js +197 -0
- package/dist/lib/agent-tools/chrome-path.js +87 -0
- package/dist/lib/agent-tools/cli-report-session.js +121 -0
- package/dist/lib/agent-tools/error-formatter.js +149 -0
- package/dist/lib/agent-tools/index.js +114 -0
- package/dist/lib/agent-tools/init-arg-utils.js +78 -0
- package/dist/lib/agent-tools/observation-artifact.js +42 -0
- package/dist/lib/agent-tools/observation-record.js +297 -0
- package/dist/lib/agent-tools/tool-defaults.js +97 -0
- package/dist/lib/agent-tools/tool-generator.js +569 -0
- package/dist/lib/agent-tools/types.js +40 -0
- package/dist/lib/agent-tools/user-prompt.js +103 -0
- package/dist/lib/cli/interrupt.js +156 -0
- package/dist/lib/cli/record-command.js +164 -0
- package/dist/lib/cli/screenshot-file.js +58 -0
- package/dist/lib/cli/verbose-ai-act.js +273 -0
- package/dist/lib/cli/verbose-screenshot.js +177 -0
- package/dist/lib/cli/verbose.js +465 -0
- package/dist/lib/env/parse-model-config.js +1 -1
- package/dist/lib/env/types.js +5 -0
- package/dist/lib/img/info.js +48 -1
- package/dist/lib/img/photon-loader.js +38 -0
- package/dist/lib/recorder.js +307 -0
- package/dist/types/agent-tools/agent-behavior-init-args.d.ts +17 -0
- package/dist/types/agent-tools/base-tools.d.ts +158 -0
- package/dist/types/agent-tools/chrome-path.d.ts +2 -0
- package/dist/types/agent-tools/cli-report-session.d.ts +12 -0
- package/dist/types/agent-tools/error-formatter.d.ts +30 -0
- package/dist/types/agent-tools/index.d.ts +9 -0
- package/dist/types/agent-tools/init-arg-utils.d.ts +13 -0
- package/dist/types/agent-tools/observation-artifact.d.ts +10 -0
- package/dist/types/agent-tools/observation-record.d.ts +38 -0
- package/dist/types/agent-tools/tool-defaults.d.ts +63 -0
- package/dist/types/agent-tools/tool-generator.d.ts +13 -0
- package/dist/types/agent-tools/types.d.ts +213 -0
- package/dist/types/agent-tools/user-prompt.d.ts +13 -0
- package/dist/types/cli/interrupt.d.ts +49 -0
- package/dist/types/cli/record-command.d.ts +3 -0
- package/dist/types/cli/screenshot-file.d.ts +10 -0
- package/dist/types/cli/verbose-ai-act.d.ts +44 -0
- package/dist/types/cli/verbose-screenshot.d.ts +10 -0
- package/dist/types/cli/verbose.d.ts +40 -0
- package/dist/types/env/types.d.ts +4 -3
- package/dist/types/img/info.d.ts +2 -0
- package/dist/types/img/photon-loader.d.ts +2 -0
- package/dist/types/mcp/types.d.ts +1 -0
- package/dist/types/recorder.d.ts +113 -0
- package/package.json +1 -1
- package/src/agent-tools/agent-behavior-init-args.ts +109 -0
- package/src/agent-tools/base-tools.ts +399 -0
- package/src/agent-tools/chrome-path.ts +74 -0
- package/src/agent-tools/cli-report-session.ts +130 -0
- package/src/agent-tools/error-formatter.ts +177 -0
- package/src/agent-tools/index.ts +9 -0
- package/src/agent-tools/init-arg-utils.ts +105 -0
- package/src/agent-tools/observation-artifact.ts +29 -0
- package/src/agent-tools/observation-record.ts +331 -0
- package/src/agent-tools/tool-defaults.ts +119 -0
- package/src/agent-tools/tool-generator.ts +866 -0
- package/src/agent-tools/types.ts +250 -0
- package/src/agent-tools/user-prompt.ts +102 -0
- package/src/cli/interrupt.ts +207 -0
- package/src/cli/record-command.ts +177 -0
- package/src/cli/screenshot-file.ts +61 -0
- package/src/cli/verbose-ai-act.ts +387 -0
- package/src/cli/verbose-screenshot.ts +269 -0
- package/src/cli/verbose.ts +753 -0
- package/src/env/types.ts +2 -0
- package/src/img/index.ts +12 -0
- package/src/img/info.ts +61 -0
- package/src/img/photon-loader.ts +5 -0
- package/src/img/transform.ts +261 -2
- package/src/mcp/types.ts +2 -0
- package/src/recorder.ts +625 -0
package/src/env/types.ts
CHANGED
|
@@ -10,6 +10,7 @@ export const MIDSCENE_DANGEROUSLY_PRINT_ALL_CONFIG =
|
|
|
10
10
|
export const MIDSCENE_DEBUG_MODE = 'MIDSCENE_DEBUG_MODE';
|
|
11
11
|
export const MIDSCENE_MCP_USE_PUPPETEER_MODE =
|
|
12
12
|
'MIDSCENE_MCP_USE_PUPPETEER_MODE';
|
|
13
|
+
export const MIDSCENE_CHROME_PATH = 'MIDSCENE_CHROME_PATH';
|
|
13
14
|
export const MIDSCENE_MCP_CHROME_PATH = 'MIDSCENE_MCP_CHROME_PATH';
|
|
14
15
|
export const MIDSCENE_MCP_ANDROID_MODE = 'MIDSCENE_MCP_ANDROID_MODE';
|
|
15
16
|
export const DOCKER_CONTAINER = 'DOCKER_CONTAINER';
|
|
@@ -195,6 +196,7 @@ export const STRING_ENV_KEYS = [
|
|
|
195
196
|
MIDSCENE_REPORT_TAG_NAME,
|
|
196
197
|
MIDSCENE_PREFERRED_LANGUAGE,
|
|
197
198
|
MATCH_BY_POSITION,
|
|
199
|
+
MIDSCENE_CHROME_PATH,
|
|
198
200
|
MIDSCENE_MCP_CHROME_PATH,
|
|
199
201
|
DOCKER_CONTAINER,
|
|
200
202
|
] as const;
|
package/src/img/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export {
|
|
2
|
+
encodedImageInfoOfBuffer,
|
|
2
3
|
imageInfoOfBase64,
|
|
3
4
|
isValidPNGImageBuffer,
|
|
4
5
|
isValidJPEGImageBuffer,
|
|
@@ -8,6 +9,10 @@ export {
|
|
|
8
9
|
} from './info';
|
|
9
10
|
export {
|
|
10
11
|
resizeAndConvertImgBuffer,
|
|
12
|
+
convertImgBufferToJpeg,
|
|
13
|
+
convertBase64ImageToJpeg,
|
|
14
|
+
constrainBase64ImageToMaxSize,
|
|
15
|
+
resizeBase64ImageToJpeg,
|
|
11
16
|
resizeImgBase64,
|
|
12
17
|
zoomForGPT4o,
|
|
13
18
|
saveBase64Image,
|
|
@@ -19,6 +24,13 @@ export {
|
|
|
19
24
|
preProcessImageUrl,
|
|
20
25
|
parseBase64,
|
|
21
26
|
createImgBase64ByFormat,
|
|
27
|
+
inferBase64ImageFormat,
|
|
28
|
+
normalizeBase64Image,
|
|
29
|
+
normalizeScreenshotBase64,
|
|
30
|
+
type NormalizeScreenshotBase64Options,
|
|
31
|
+
type ConstrainBase64ImageToMaxSizeOptions,
|
|
32
|
+
type JpegBase64DataUrl,
|
|
33
|
+
type ResizeBase64ImageToJpegOptions,
|
|
22
34
|
} from './transform';
|
|
23
35
|
export {
|
|
24
36
|
processImageElementInfo,
|
package/src/img/info.ts
CHANGED
|
@@ -1,9 +1,70 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
|
+
import { Buffer } from 'node:buffer';
|
|
2
3
|
import type { Size } from '../types';
|
|
3
4
|
import getPhoton from './get-photon';
|
|
4
5
|
|
|
5
6
|
export interface ImageInfo extends Size {}
|
|
6
7
|
|
|
8
|
+
const isJpegStartOfFrameMarker = (marker: number): boolean =>
|
|
9
|
+
marker >= 0xc0 &&
|
|
10
|
+
marker <= 0xcf &&
|
|
11
|
+
marker !== 0xc4 &&
|
|
12
|
+
marker !== 0xc8 &&
|
|
13
|
+
marker !== 0xcc;
|
|
14
|
+
|
|
15
|
+
function jpegInfoFromBuffer(imageBuffer: Buffer): ImageInfo {
|
|
16
|
+
let offset = 2;
|
|
17
|
+
while (offset < imageBuffer.length) {
|
|
18
|
+
if (imageBuffer[offset] !== 0xff) {
|
|
19
|
+
offset += 1;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
while (offset < imageBuffer.length && imageBuffer[offset] === 0xff) {
|
|
24
|
+
offset += 1;
|
|
25
|
+
}
|
|
26
|
+
if (offset >= imageBuffer.length) break;
|
|
27
|
+
|
|
28
|
+
const marker = imageBuffer[offset];
|
|
29
|
+
offset += 1;
|
|
30
|
+
if (marker === 0xd9 || marker === 0xda) break;
|
|
31
|
+
if (
|
|
32
|
+
marker === 0x01 ||
|
|
33
|
+
marker === 0xd8 ||
|
|
34
|
+
(marker >= 0xd0 && marker <= 0xd7)
|
|
35
|
+
) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (offset + 2 > imageBuffer.length) break;
|
|
39
|
+
|
|
40
|
+
const segmentLength = imageBuffer.readUInt16BE(offset);
|
|
41
|
+
if (segmentLength < 2 || offset + segmentLength > imageBuffer.length) break;
|
|
42
|
+
if (isJpegStartOfFrameMarker(marker)) {
|
|
43
|
+
if (segmentLength < 7) break;
|
|
44
|
+
const height = imageBuffer.readUInt16BE(offset + 3);
|
|
45
|
+
const width = imageBuffer.readUInt16BE(offset + 5);
|
|
46
|
+
assert(width && height, 'Invalid image: cannot get width or height');
|
|
47
|
+
return { width, height };
|
|
48
|
+
}
|
|
49
|
+
offset += segmentLength;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw new Error('Invalid image: cannot get JPEG width or height');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function encodedImageInfoOfBuffer(imageBuffer: Buffer): ImageInfo {
|
|
56
|
+
if (isValidPNGImageBuffer(imageBuffer)) {
|
|
57
|
+
const width = imageBuffer.readUInt32BE(16);
|
|
58
|
+
const height = imageBuffer.readUInt32BE(20);
|
|
59
|
+
assert(width && height, 'Invalid image: cannot get width or height');
|
|
60
|
+
return { width, height };
|
|
61
|
+
}
|
|
62
|
+
if (isValidJPEGImageBuffer(imageBuffer)) {
|
|
63
|
+
return jpegInfoFromBuffer(imageBuffer);
|
|
64
|
+
}
|
|
65
|
+
throw new Error('Invalid image: unsupported format');
|
|
66
|
+
}
|
|
67
|
+
|
|
7
68
|
/**
|
|
8
69
|
* Retrieves the dimensions of an image from a base64-encoded string
|
|
9
70
|
*
|
package/src/img/transform.ts
CHANGED
|
@@ -4,11 +4,12 @@ import { readFileSync } from 'node:fs';
|
|
|
4
4
|
import { writeFile } from 'node:fs/promises';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import type { PhotonImage as PhotonImageType } from '@silvia-odwyer/photon-node';
|
|
7
|
-
import type { Rect } from '
|
|
7
|
+
import type { Rect, Size } from '../types';
|
|
8
8
|
import { getDebug } from '../logger';
|
|
9
9
|
import { ifInNode } from '../utils';
|
|
10
10
|
import getPhoton from './get-photon';
|
|
11
11
|
import getSharp from './get-sharp';
|
|
12
|
+
import { encodedImageInfoOfBuffer } from './info';
|
|
12
13
|
|
|
13
14
|
const imgDebug = getDebug('img');
|
|
14
15
|
|
|
@@ -154,10 +155,268 @@ export async function resizeAndConvertImgBuffer(
|
|
|
154
155
|
};
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
const rawBase64BodyPattern = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
159
|
+
const base64ImageDataUrlPattern =
|
|
160
|
+
/^data:image\/(?:png|jpe?g);base64,[A-Za-z0-9+/=\s]+$/i;
|
|
161
|
+
const supportedScreenshotDataUriPattern =
|
|
162
|
+
/^data:image\/(png|jpe?g);base64,([A-Za-z0-9+/=\s]+)$/i;
|
|
163
|
+
|
|
164
|
+
function normalizeBase64Body(body: string): string {
|
|
165
|
+
return body.replace(/\s/g, '');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function detectImageMimeTypeFromBuffer(buffer: Buffer): string | undefined {
|
|
169
|
+
if (
|
|
170
|
+
buffer.length >= 8 &&
|
|
171
|
+
buffer[0] === 0x89 &&
|
|
172
|
+
buffer[1] === 0x50 &&
|
|
173
|
+
buffer[2] === 0x4e &&
|
|
174
|
+
buffer[3] === 0x47 &&
|
|
175
|
+
buffer[4] === 0x0d &&
|
|
176
|
+
buffer[5] === 0x0a &&
|
|
177
|
+
buffer[6] === 0x1a &&
|
|
178
|
+
buffer[7] === 0x0a
|
|
179
|
+
) {
|
|
180
|
+
return 'image/png';
|
|
181
|
+
}
|
|
182
|
+
if (
|
|
183
|
+
buffer.length >= 3 &&
|
|
184
|
+
buffer[0] === 0xff &&
|
|
185
|
+
buffer[1] === 0xd8 &&
|
|
186
|
+
buffer[2] === 0xff
|
|
187
|
+
) {
|
|
188
|
+
return 'image/jpeg';
|
|
189
|
+
}
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function inferBase64ImageFormat(base64Body: string): string {
|
|
194
|
+
const buffer = Buffer.from(normalizeBase64Body(base64Body), 'base64');
|
|
195
|
+
const mimeType = detectImageMimeTypeFromBuffer(buffer);
|
|
196
|
+
if (mimeType === 'image/jpeg') return 'jpeg';
|
|
197
|
+
if (mimeType === 'image/png') return 'png';
|
|
198
|
+
return 'png';
|
|
199
|
+
}
|
|
200
|
+
|
|
157
201
|
export const createImgBase64ByFormat = (format: string, body: string) => {
|
|
158
|
-
return `data:image/${format};base64,${body}`;
|
|
202
|
+
return `data:image/${format};base64,${normalizeBase64Body(body)}`;
|
|
159
203
|
};
|
|
160
204
|
|
|
205
|
+
export interface NormalizeScreenshotBase64Options {
|
|
206
|
+
label?: string;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export const normalizeScreenshotBase64 = (
|
|
210
|
+
base64: string,
|
|
211
|
+
options?: NormalizeScreenshotBase64Options,
|
|
212
|
+
) => {
|
|
213
|
+
const label = options?.label ?? 'screenshot base64';
|
|
214
|
+
const trimmedBase64 = base64.trim();
|
|
215
|
+
if (!trimmedBase64) {
|
|
216
|
+
throw new Error(`${label} cannot be empty`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const dataUriMatch = trimmedBase64.match(supportedScreenshotDataUriPattern);
|
|
220
|
+
if (dataUriMatch) {
|
|
221
|
+
const imageFormat =
|
|
222
|
+
dataUriMatch[1].toLowerCase() === 'jpg'
|
|
223
|
+
? 'jpeg'
|
|
224
|
+
: dataUriMatch[1].toLowerCase();
|
|
225
|
+
const body = normalizeBase64Body(dataUriMatch[2]);
|
|
226
|
+
if (!body) {
|
|
227
|
+
throw new Error(`${label} cannot be empty`);
|
|
228
|
+
}
|
|
229
|
+
return createImgBase64ByFormat(imageFormat, body);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (trimmedBase64.startsWith('data:')) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
`${label} must be a PNG/JPEG data URI or raw PNG base64 string`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!rawBase64BodyPattern.test(normalizeBase64Body(trimmedBase64))) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`${label} must be a PNG/JPEG data URI or raw PNG base64 string`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return createImgBase64ByFormat('png', trimmedBase64);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export const normalizeBase64Image = (base64: string) => {
|
|
248
|
+
const trimmedBase64 = base64.trim();
|
|
249
|
+
if (base64ImageDataUrlPattern.test(trimmedBase64)) {
|
|
250
|
+
return trimmedBase64.replace(/\s/g, '');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const base64Body = normalizeBase64Body(trimmedBase64);
|
|
254
|
+
assert(base64Body, 'base64 image must include image data');
|
|
255
|
+
return createImgBase64ByFormat(
|
|
256
|
+
inferBase64ImageFormat(base64Body),
|
|
257
|
+
base64Body,
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export type JpegBase64DataUrl = `data:image/jpeg;base64,${string}`;
|
|
262
|
+
|
|
263
|
+
export interface ResizeBase64ImageToJpegOptions {
|
|
264
|
+
sourceSize?: Size;
|
|
265
|
+
targetSize: Size;
|
|
266
|
+
jpegQuality?: number;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface ConstrainBase64ImageToMaxSizeOptions {
|
|
270
|
+
maxSize: number;
|
|
271
|
+
jpegQuality?: number;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function assertValidJpegQuality(jpegQuality: number): void {
|
|
275
|
+
if (!Number.isInteger(jpegQuality) || jpegQuality < 1 || jpegQuality > 100) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`jpegQuality must be an integer between 1 and 100. Received: ${jpegQuality}`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function assertValidImageSize(size: Size, label: string): void {
|
|
283
|
+
if (
|
|
284
|
+
!Number.isInteger(size.width) ||
|
|
285
|
+
!Number.isInteger(size.height) ||
|
|
286
|
+
size.width <= 0 ||
|
|
287
|
+
size.height <= 0
|
|
288
|
+
) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
`${label} width and height must be positive integers. Received width: ${size.width}, height: ${size.height}`,
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export async function convertImgBufferToJpeg(
|
|
296
|
+
inputData: Buffer,
|
|
297
|
+
jpegQuality = 90,
|
|
298
|
+
): Promise<Buffer> {
|
|
299
|
+
assertValidJpegQuality(jpegQuality);
|
|
300
|
+
|
|
301
|
+
if (ifInNode) {
|
|
302
|
+
try {
|
|
303
|
+
const Sharp = await getSharp();
|
|
304
|
+
return await Sharp(inputData).jpeg({ quality: jpegQuality }).toBuffer();
|
|
305
|
+
} catch (error) {
|
|
306
|
+
imgDebug('Sharp failed, falling back to Photon:', error);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const { PhotonImage } = await getPhoton();
|
|
311
|
+
const inputBytes = new Uint8Array(inputData);
|
|
312
|
+
const bytesliceResult = PhotonImage.new_from_byteslice(inputBytes);
|
|
313
|
+
const image =
|
|
314
|
+
bytesliceResult instanceof Promise
|
|
315
|
+
? await bytesliceResult
|
|
316
|
+
: bytesliceResult;
|
|
317
|
+
try {
|
|
318
|
+
return Buffer.from(image.get_bytes_jpeg(jpegQuality));
|
|
319
|
+
} finally {
|
|
320
|
+
image.free();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export async function convertBase64ImageToJpeg(
|
|
325
|
+
inputBase64: string,
|
|
326
|
+
jpegQuality = 90,
|
|
327
|
+
): Promise<JpegBase64DataUrl> {
|
|
328
|
+
assertValidJpegQuality(jpegQuality);
|
|
329
|
+
const normalized = normalizeBase64Image(inputBase64);
|
|
330
|
+
const { body } = parseBase64(normalized);
|
|
331
|
+
const imageBuffer = Buffer.from(body, 'base64');
|
|
332
|
+
const detectedMimeType = detectImageMimeTypeFromBuffer(imageBuffer);
|
|
333
|
+
if (detectedMimeType === 'image/jpeg') {
|
|
334
|
+
return createImgBase64ByFormat('jpeg', body) as JpegBase64DataUrl;
|
|
335
|
+
}
|
|
336
|
+
if (detectedMimeType !== 'image/png') {
|
|
337
|
+
throw new Error(
|
|
338
|
+
`inputBase64 must contain a PNG or JPEG image. Detected: ${detectedMimeType ?? 'unsupported format'}`,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const jpegBuffer = await convertImgBufferToJpeg(imageBuffer, jpegQuality);
|
|
343
|
+
return createImgBase64ByFormat(
|
|
344
|
+
'jpeg',
|
|
345
|
+
jpegBuffer.toString('base64'),
|
|
346
|
+
) as JpegBase64DataUrl;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export async function resizeBase64ImageToJpeg(
|
|
350
|
+
inputBase64: string,
|
|
351
|
+
options: ResizeBase64ImageToJpegOptions,
|
|
352
|
+
): Promise<JpegBase64DataUrl> {
|
|
353
|
+
const jpegQuality = options.jpegQuality ?? 90;
|
|
354
|
+
assertValidJpegQuality(jpegQuality);
|
|
355
|
+
assertValidImageSize(options.targetSize, 'targetSize');
|
|
356
|
+
|
|
357
|
+
const normalized = normalizeBase64Image(inputBase64);
|
|
358
|
+
const { body, mimeType } = parseBase64(normalized);
|
|
359
|
+
const imageBuffer = Buffer.from(body, 'base64');
|
|
360
|
+
const sourceSize = encodedImageInfoOfBuffer(imageBuffer);
|
|
361
|
+
if (options.sourceSize) {
|
|
362
|
+
assertValidImageSize(options.sourceSize, 'sourceSize');
|
|
363
|
+
if (
|
|
364
|
+
options.sourceSize.width !== sourceSize.width ||
|
|
365
|
+
options.sourceSize.height !== sourceSize.height
|
|
366
|
+
) {
|
|
367
|
+
throw new Error(
|
|
368
|
+
`sourceSize ${options.sourceSize.width}x${options.sourceSize.height} does not match encoded image dimensions ${sourceSize.width}x${sourceSize.height}`,
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const dimensionsUnchanged =
|
|
374
|
+
sourceSize.width === options.targetSize.width &&
|
|
375
|
+
sourceSize.height === options.targetSize.height;
|
|
376
|
+
if (dimensionsUnchanged) {
|
|
377
|
+
return convertBase64ImageToJpeg(normalized, jpegQuality);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const { buffer } = await resizeAndConvertImgBuffer(
|
|
381
|
+
mimeType.split('/')[1],
|
|
382
|
+
imageBuffer,
|
|
383
|
+
options.targetSize,
|
|
384
|
+
);
|
|
385
|
+
return createImgBase64ByFormat(
|
|
386
|
+
'jpeg',
|
|
387
|
+
buffer.toString('base64'),
|
|
388
|
+
) as JpegBase64DataUrl;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export async function constrainBase64ImageToMaxSize(
|
|
392
|
+
inputBase64: string,
|
|
393
|
+
options: ConstrainBase64ImageToMaxSizeOptions,
|
|
394
|
+
): Promise<string> {
|
|
395
|
+
if (!Number.isInteger(options.maxSize) || options.maxSize <= 0) {
|
|
396
|
+
throw new Error(
|
|
397
|
+
`maxSize must be a positive integer. Received: ${options.maxSize}`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const normalized = normalizeBase64Image(inputBase64);
|
|
402
|
+
const { body } = parseBase64(normalized);
|
|
403
|
+
const sourceSize = encodedImageInfoOfBuffer(Buffer.from(body, 'base64'));
|
|
404
|
+
const largestDimension = Math.max(sourceSize.width, sourceSize.height);
|
|
405
|
+
if (largestDimension <= options.maxSize) {
|
|
406
|
+
return normalized;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const scale = options.maxSize / largestDimension;
|
|
410
|
+
return resizeBase64ImageToJpeg(normalized, {
|
|
411
|
+
targetSize: {
|
|
412
|
+
width: Math.max(1, Math.round(sourceSize.width * scale)),
|
|
413
|
+
height: Math.max(1, Math.round(sourceSize.height * scale)),
|
|
414
|
+
},
|
|
415
|
+
sourceSize,
|
|
416
|
+
jpegQuality: options.jpegQuality,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
161
420
|
export async function resizeImgBase64(
|
|
162
421
|
inputBase64: string,
|
|
163
422
|
newSize: {
|
package/src/mcp/types.ts
CHANGED
|
@@ -111,6 +111,8 @@ export interface BaseAgent {
|
|
|
111
111
|
msg?: string,
|
|
112
112
|
options?: Record<string, unknown>,
|
|
113
113
|
) => Promise<unknown>;
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
115
|
+
aiQuery?: (demand: any, opt?: any) => Promise<any>;
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
/**
|