diff --git a/.changeset/beige-taxes-punch.md b/.changeset/beige-taxes-punch.md new file mode 100644 index 000000000..45235e16d --- /dev/null +++ b/.changeset/beige-taxes-punch.md @@ -0,0 +1,5 @@ +--- +"@browserbasehq/stagehand-evals": patch +--- + +Update screenshot collector in agent evals cli diff --git a/.prettierignore b/.prettierignore index e42930bb9..98ad8477c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,4 +12,8 @@ packages/core/lib/dom/build/ packages/core/lib/v3/dom/build/ packages/evals/dist/ packages/docs/ -*.min.js \ No newline at end of file +*.min.js +.browserbase/ +.browserbase/** +**/.browserbase/ +**/.browserbase/** \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index c44685b43..6b45c9e88 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -14,6 +14,9 @@ export default [ "packages/core/lib/v3/dom/build/**", "**/*.config.js", "**/*.config.mjs", + ".browserbase/**", + "**/.browserbase/**", + "**/*.json", ], }, pluginJs.configs.recommended, diff --git a/package.json b/package.json index a958ca9a2..5757c9bf6 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "build": "turbo run build", "build:cli": "turbo run build:cli", "lint": "turbo run lint", - "format": "turbo run format", + "format": "prettier --write .", "prettier": "prettier --write .", "eslint": "eslint .", "test": "turbo run test", diff --git a/packages/core/lib/utils.ts b/packages/core/lib/utils.ts index 13bf0c8b3..42719672b 100644 --- a/packages/core/lib/utils.ts +++ b/packages/core/lib/utils.ts @@ -7,6 +7,7 @@ import { ModelProvider } from "./v3/types/public/model"; import { ZodPathSegments } from "./v3/types/private/internal"; import type { StagehandZodSchema } from "./v3/zodCompat"; import { isZod4Schema } from "./v3/zodCompat"; +import sharp from "sharp"; const ID_PATTERN = /^\d+-\d+$/; @@ -837,3 +838,21 @@ export function jsonSchemaToZod(schema: JsonSchema): ZodTypeAny { return z.any(); } } + +export async function imageResize( + img: Buffer, + scaleFactor: number, +): Promise { + const metadata = await sharp(img).metadata(); + // calculate new dimensions + const width = Math.round(metadata.width * scaleFactor); + const height = Math.round(metadata.height * scaleFactor); + return await sharp(img) + .resize(width, height, { fit: "inside", kernel: sharp.kernel.lanczos3 }) + .png({ + compressionLevel: 9, + adaptiveFiltering: true, + palette: true, + }) + .toBuffer(); +} diff --git a/packages/core/lib/v3/handlers/v3AgentHandler.ts b/packages/core/lib/v3/handlers/v3AgentHandler.ts index f418f396d..9e7cea9d7 100644 --- a/packages/core/lib/v3/handlers/v3AgentHandler.ts +++ b/packages/core/lib/v3/handlers/v3AgentHandler.ts @@ -171,6 +171,17 @@ export class V3AgentHandler { } } state.currentPageUrl = (await this.v3.context.awaitActivePage()).url(); + + // Capture screenshot after tool execution and emit event + try { + await this.captureAndEmitScreenshot(); + } catch (e) { + this.logger({ + category: "agent", + message: `Warning: Failed to capture screenshot: ${getErrorMessage(e)}`, + level: 1, + }); + } } if (userCallback) { @@ -448,4 +459,21 @@ export class V3AgentHandler { } return stepCountIs(maxSteps)(result); } + + /** + * Capture a screenshot and emit it via the event bus + */ + private async captureAndEmitScreenshot(): Promise { + try { + const page = await this.v3.context.awaitActivePage(); + const screenshot = await page.screenshot({ fullPage: false }); + this.v3.bus.emit("agent_screensot_taken_event", screenshot); + } catch (error) { + this.logger({ + category: "agent", + message: `Error capturing screenshot: ${getErrorMessage(error)}`, + level: 0, + }); + } + } } diff --git a/packages/core/lib/v3/handlers/v3CuaAgentHandler.ts b/packages/core/lib/v3/handlers/v3CuaAgentHandler.ts index f06b99c3e..b6595187f 100644 --- a/packages/core/lib/v3/handlers/v3CuaAgentHandler.ts +++ b/packages/core/lib/v3/handlers/v3CuaAgentHandler.ts @@ -23,8 +23,6 @@ export class V3CuaAgentHandler { private agentClient: AgentClient; private options: AgentHandlerOptions; private highlightCursor: boolean; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private screenshotCollector?: any; constructor( v3: V3, @@ -545,9 +543,8 @@ export class V3CuaAgentHandler { try { const page = await this.v3.context.awaitActivePage(); const base64Image = await page.screenshot({ fullPage: false }); - if (this.screenshotCollector) { - this.screenshotCollector.addScreenshot(base64Image); - } + // Emit screenshot event via the bus + this.v3.bus.emit("agent_screensot_taken_event", base64Image); const currentUrl = page.url(); return await this.agentClient.captureScreenshot({ base64Image, @@ -571,20 +568,4 @@ export class V3CuaAgentHandler { // Best-effort only } } - - /** - * Set the screenshot collector for this agent handler - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - setScreenshotCollector(collector: any): void { - this.screenshotCollector = collector; - } - - /** - * Get the screenshot collector - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getScreenshotCollector(): any { - return this.screenshotCollector; - } } diff --git a/packages/core/lib/v3/v3.ts b/packages/core/lib/v3/v3.ts index c17ca0996..cec0048d0 100644 --- a/packages/core/lib/v3/v3.ts +++ b/packages/core/lib/v3/v3.ts @@ -1,4 +1,5 @@ import dotenv from "dotenv"; +import { EventEmitter } from "events"; import fs from "fs"; import os from "os"; import path from "path"; @@ -136,6 +137,12 @@ export class V3 { private observeHandler: ObserveHandler | null = null; private ctx: V3Context | null = null; public llmClient!: LLMClient; + + /** + * Event bus for internal communication. + * Emits events like 'screenshot' when screenshots are captured during agent execution. + */ + public readonly bus: EventEmitter = new EventEmitter(); private modelName: AvailableModel; private modelClientOptions: ClientOptions; private llmProvider: LLMProvider; diff --git a/packages/core/package.json b/packages/core/package.json index 81d308ce6..0385d370e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -57,6 +57,7 @@ "pino": "^9.6.0", "pino-pretty": "^13.0.0", "playwright": "^1.52.0", + "sharp": "^0.34.5", "ws": "^8.18.0", "zod-to-json-schema": "^3.25.0" }, diff --git a/packages/evals/index.eval.ts b/packages/evals/index.eval.ts index 1580769f7..9f506127d 100644 --- a/packages/evals/index.eval.ts +++ b/packages/evals/index.eval.ts @@ -386,13 +386,7 @@ const generateFilteredTestcases = (): Testcase[] => { // Pass full EvalInput to the task (data-driven params available via input.params) let result; try { - result = await taskFunction({ - // ...taskInput, - v3: v3Input?.v3, - v3Agent: v3Input?.agent, - logger: v3Input?.logger, - v3Input, - }); + result = await taskFunction({ ...v3Input, input }); // Log result to console if (result && result._success) { console.log(`✅ ${input.name}: Passed`); diff --git a/packages/evals/package.json b/packages/evals/package.json index c6c07829a..bbd5b2c23 100644 --- a/packages/evals/package.json +++ b/packages/evals/package.json @@ -13,11 +13,12 @@ "format": "prettier --write ." }, "dependencies": { + "@ai-sdk/provider": "^2.0.0", "@browserbasehq/stagehand": "workspace:*", "ai": "^5.0.0", - "@ai-sdk/provider": "^2.0.0", - "openai": "^4.87.1", "dotenv": "16.4.5", + "openai": "^4.87.1", + "sharp": "^0.34.5", "zod": "^4.1.8" }, "devDependencies": { diff --git a/packages/evals/tasks/agent/onlineMind2Web.ts b/packages/evals/tasks/agent/onlineMind2Web.ts index 654d6f8a8..987dd29a4 100644 --- a/packages/evals/tasks/agent/onlineMind2Web.ts +++ b/packages/evals/tasks/agent/onlineMind2Web.ts @@ -37,6 +37,7 @@ export const onlineMind2Web: EvalFunction = async ({ }); const agent = v3.agent({ + cua: true, model: modelName, systemPrompt: `You are a helpful assistant that must solve the task by browsing. At the end, produce a single line: "Final Answer: " summarizing the requested result (e.g., score, list, or text). Current page: ${await page.title()}. ALWAYS OPERATE WITHIN THE PAGE OPENED BY THE USER, WHICHEVER TASK YOU ARE ATTEMPTING TO COMPLETE CAN BE ACCOMPLISHED WITHIN THE PAGE.`, }); @@ -50,6 +51,12 @@ export const onlineMind2Web: EvalFunction = async ({ captureOnNavigation: true, // Also capture on page navigation }); + // Subscribe to screenshot events from the agent via the bus + const screenshotHandler = (buffer: Buffer) => { + screenshotCollector.addScreenshot(buffer); + }; + v3.bus.on("agent_screensot_taken_event", screenshotHandler); + screenshotCollector.start(); const agentResult = await agent.execute({ @@ -57,7 +64,8 @@ export const onlineMind2Web: EvalFunction = async ({ maxSteps: Number(process.env.AGENT_EVAL_MAX_STEPS) || 50, }); - // Stop collecting and get all screenshots + // Stop collecting, clean up event listener, and get all screenshots + v3.bus.off("agent_screensot_taken_event", screenshotHandler); const screenshots = screenshotCollector.stop(); logger.log({ diff --git a/packages/evals/types/screenshotCollector.ts b/packages/evals/types/screenshotCollector.ts index c4bc0332d..16d468521 100644 --- a/packages/evals/types/screenshotCollector.ts +++ b/packages/evals/types/screenshotCollector.ts @@ -1,6 +1,7 @@ export interface ScreenshotCollectorOptions { interval?: number; maxScreenshots?: number; + /* @deprecated for V3, there's a new method to intercept screenshots by injecting into the agent loop */ captureOnNavigation?: boolean; } diff --git a/packages/evals/utils/ScreenshotCollector.ts b/packages/evals/utils/ScreenshotCollector.ts index df2079fbb..26829c886 100644 --- a/packages/evals/utils/ScreenshotCollector.ts +++ b/packages/evals/utils/ScreenshotCollector.ts @@ -1,24 +1,26 @@ -import { ScreenshotCapablePage } from "../types/screenshotCollector"; +import { Page } from "@browserbasehq/stagehand"; +import sharp from "sharp"; import { ScreenshotCollectorOptions } from "../types/screenshotCollector"; export class ScreenshotCollector { private screenshots: Buffer[] = []; - private page: ScreenshotCapablePage; + private page: Page; private interval: number; private maxScreenshots: number; private captureOnNavigation: boolean; private intervalId?: NodeJS.Timeout; private navigationListeners: Array<() => void> = []; private isCapturing: boolean = false; + private lastScreenshot?: Buffer; + private ssimThreshold: number = 0.75; + private mseThreshold: number = 30; - constructor( - page: ScreenshotCapablePage, - options: ScreenshotCollectorOptions = {}, - ) { + constructor(page: Page, options: ScreenshotCollectorOptions = {}) { this.page = page; this.interval = options.interval || 5000; this.maxScreenshots = options.maxScreenshots || 10; - this.captureOnNavigation = options.captureOnNavigation ?? true; + // Capture on navigation is deprecated for V3 pages + this.captureOnNavigation = options.captureOnNavigation ?? false; } start(): void { @@ -26,25 +28,17 @@ export class ScreenshotCollector { return; } - this.intervalId = setInterval(async () => { - await this.captureScreenshot("interval"); + // Set up time-based screenshot capture + this.intervalId = setInterval(() => { + this.captureScreenshot("interval").catch((error) => { + console.error("Interval screenshot failed:", error); + }); }, this.interval); - if (this.captureOnNavigation && this.page.on && this.page.off) { - const loadListener = () => this.captureScreenshot("load"); - const domContentListener = () => - this.captureScreenshot("domcontentloaded"); - - this.page.on("load", loadListener); - this.page.on("domcontentloaded", domContentListener); - - this.navigationListeners = [ - () => this.page!.off!("load", loadListener), - () => this.page!.off!("domcontentloaded", domContentListener), - ]; - } - - this.captureScreenshot("initial"); + // Capture initial screenshot without blocking + this.captureScreenshot("initial").catch((error) => { + console.error("Failed to capture initial screenshot:", error); + }); } stop(): Buffer[] { @@ -56,8 +50,10 @@ export class ScreenshotCollector { this.navigationListeners.forEach((removeListener) => removeListener()); this.navigationListeners = []; - this.captureScreenshot("final"); - + // Capture final screenshot without blocking + this.captureScreenshot("final").catch((error) => { + console.error("Failed to capture final screenshot:", error); + }); return this.getScreenshots(); } @@ -65,24 +61,43 @@ export class ScreenshotCollector { if (this.isCapturing) { return; } - this.isCapturing = true; try { const screenshot = await this.page.screenshot(); - const buffer = - typeof screenshot === "string" - ? Buffer.from(screenshot, "base64") - : (screenshot as Buffer); - this.screenshots.push(buffer); - - if (this.screenshots.length > this.maxScreenshots) { - this.screenshots.shift(); + + // Check if we should keep this screenshot based on image diff + let shouldKeep = true; + if (this.lastScreenshot && trigger !== "initial" && trigger !== "final") { + try { + // First do a quick MSE check + const mse = await this.calculateMSE(this.lastScreenshot, screenshot); + if (mse < this.mseThreshold) { + // Very similar, skip + shouldKeep = false; + } else { + // Significant difference detected, verify with SSIM + const ssim = await this.calculateSSIM( + this.lastScreenshot, + screenshot, + ); + shouldKeep = ssim < this.ssimThreshold; + } + } catch (error) { + // If comparison fails, keep the screenshot + console.error("Image comparison failed:", error); + shouldKeep = true; + } } - console.log( - `Screenshot captured (trigger: ${trigger}), total: ${this.screenshots.length}`, - ); + if (shouldKeep) { + this.screenshots.push(screenshot); + this.lastScreenshot = screenshot; + + if (this.screenshots.length > this.maxScreenshots) { + this.screenshots.shift(); + } + } } catch (error) { console.error(`Failed to capture screenshot (${trigger}):`, error); } finally { @@ -101,4 +116,120 @@ export class ScreenshotCollector { clear(): void { this.screenshots = []; } + + /** + * Manually add a screenshot to the collection + * @param screenshot The screenshot buffer to add + */ + async addScreenshot(screenshot: Buffer): Promise { + // Prevent concurrent processing + if (this.isCapturing) { + return; + } + this.isCapturing = true; + + try { + // Apply MSE/SSIM logic to decide if we should keep this screenshot + let shouldKeep = true; + if (this.lastScreenshot) { + try { + // First do a quick MSE check + const mse = await this.calculateMSE(this.lastScreenshot, screenshot); + if (mse < this.mseThreshold) { + // Very similar, skip + shouldKeep = false; + } else { + // Significant difference detected, verify with SSIM + const ssim = await this.calculateSSIM( + this.lastScreenshot, + screenshot, + ); + shouldKeep = ssim < this.ssimThreshold; + } + } catch (error) { + // If comparison fails, keep the screenshot + console.error("Image comparison failed:", error); + shouldKeep = true; + } + } + + if (shouldKeep) { + this.screenshots.push(screenshot); + this.lastScreenshot = screenshot; + + if (this.screenshots.length > this.maxScreenshots) { + this.screenshots.shift(); + } + } + } finally { + this.isCapturing = false; + } + } + + private async calculateMSE(img1: Buffer, img2: Buffer): Promise { + try { + // Resize images for faster comparison + const size = { width: 400, height: 300 }; + const data1 = await sharp(img1).resize(size).raw().toBuffer(); + const data2 = await sharp(img2).resize(size).raw().toBuffer(); + + if (data1.length !== data2.length) return Number.MAX_SAFE_INTEGER; + + let sum = 0; + for (let i = 0; i < data1.length; i++) { + const diff = data1[i] - data2[i]; + sum += diff * diff; + } + + return sum / data1.length; + } catch { + // If sharp is not available, assume images are different + return Number.MAX_SAFE_INTEGER; + } + } + + private async calculateSSIM(img1: Buffer, img2: Buffer): Promise { + try { + // Resize and convert to grayscale for SSIM calculation + const size = { width: 400, height: 300 }; + const gray1 = await sharp(img1).resize(size).grayscale().raw().toBuffer(); + const gray2 = await sharp(img2).resize(size).grayscale().raw().toBuffer(); + + if (gray1.length !== gray2.length) return 0; + + // Simplified SSIM calculation + const c1 = 0.01 * 0.01; + const c2 = 0.03 * 0.03; + + let sum1 = 0, + sum2 = 0, + sum1_sq = 0, + sum2_sq = 0, + sum12 = 0; + const N = gray1.length; + + for (let i = 0; i < N; i++) { + sum1 += gray1[i]; + sum2 += gray2[i]; + sum1_sq += gray1[i] * gray1[i]; + sum2_sq += gray2[i] * gray2[i]; + sum12 += gray1[i] * gray2[i]; + } + + const mean1 = sum1 / N; + const mean2 = sum2 / N; + const var1 = sum1_sq / N - mean1 * mean1; + const var2 = sum2_sq / N - mean2 * mean2; + const cov12 = sum12 / N - mean1 * mean2; + + const numerator = (2 * mean1 * mean2 + c1) * (2 * cov12 + c2); + const denominator = + (mean1 * mean1 + mean2 * mean2 + c1) * (var1 + var2 + c2); + + return numerator / denominator; + } catch { + // If sharp is not available, assume images are different + return 0; + } + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d540ac6b..582133952 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,6 +83,9 @@ importers: playwright: specifier: ^1.52.0 version: 1.54.2 + sharp: + specifier: ^0.34.5 + version: 0.34.5 uuid: specifier: ^11.1.0 version: 11.1.0 @@ -95,7 +98,7 @@ importers: optionalDependencies: '@ai-sdk/anthropic': specifier: ^2.0.34 - version: 2.0.34(zod@4.1.12) + version: 2.0.53(zod@4.1.12) '@ai-sdk/azure': specifier: ^2.0.54 version: 2.0.54(zod@4.1.12) @@ -223,6 +226,9 @@ importers: openai: specifier: ^4.87.1 version: 4.96.2(ws@8.18.3(bufferutil@4.0.9))(zod@4.1.12) + sharp: + specifier: ^0.34.5 + version: 0.34.5 zod: specifier: ^4.1.8 version: 4.1.12 @@ -309,12 +315,6 @@ importers: packages: - '@ai-sdk/anthropic@2.0.34': - resolution: {integrity: sha512-ayiKwCRVubQonbMX0YBQLtJKEZ7gziW35N6Md3qwdOqEq17JGz6vjKiba9RyYa/iIVT535n6ChLtHY/vTA7ImA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/anthropic@2.0.53': resolution: {integrity: sha512-ih7NV+OFSNWZCF+tYYD7ovvvM+gv7TRKQblpVohg2ipIwC9Y0TirzocJVREzZa/v9luxUwFbsPji++DUDWWxsg==} engines: {node: '>=18'} @@ -527,8 +527,8 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@emnapi/runtime@1.4.3': - resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} @@ -912,111 +912,248 @@ packages: resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} engines: {node: '>=18.18'} + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + '@img/sharp-darwin-arm64@0.33.5': resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + '@img/sharp-darwin-x64@0.33.5': resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.0.4': resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} cpu: [arm64] os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + '@img/sharp-libvips-darwin-x64@1.0.4': resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} cpu: [x64] os: [darwin] + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + '@img/sharp-libvips-linux-arm64@1.0.4': resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] os: [linux] + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + '@img/sharp-libvips-linux-arm@1.0.5': resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] os: [linux] + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + '@img/sharp-libvips-linux-s390x@1.0.4': resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} cpu: [s390x] os: [linux] + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + '@img/sharp-libvips-linux-x64@1.0.4': resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] os: [linux] + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} cpu: [arm64] os: [linux] + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + '@img/sharp-libvips-linuxmusl-x64@1.0.4': resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} cpu: [x64] os: [linux] + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + '@img/sharp-linux-arm64@0.33.5': resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + '@img/sharp-linux-arm@0.33.5': resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + '@img/sharp-linux-s390x@0.33.5': resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + '@img/sharp-linux-x64@0.33.5': resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + '@img/sharp-linuxmusl-arm64@0.33.5': resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + '@img/sharp-linuxmusl-x64@0.33.5': resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + '@img/sharp-wasm32@0.33.5': resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + '@img/sharp-win32-ia32@0.33.5': resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + '@img/sharp-win32-x64@0.33.5': resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@inquirer/checkbox@4.1.5': resolution: {integrity: sha512-swPczVU+at65xa5uPfNP9u3qx/alNwiaykiI/ExpsmMSQW55trmZcwhYWzw/7fj+n6Q8z1eENvR7vFfq9oPSAQ==} engines: {node: '>=18'} @@ -2514,8 +2651,8 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} - detect-libc@2.0.4: - resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} detect-port@1.6.1: @@ -4564,7 +4701,7 @@ packages: puppeteer@22.15.0: resolution: {integrity: sha512-XjCY1SiSEi1T7iSYuxS82ft85kwDJUS7wj1Z0eGVXKdtr5g4xnVcbjwxhq5xBnpK/E7x1VZZoJDxpjAOasHT4Q==} engines: {node: '>=18'} - deprecated: < 24.15.0 is no longer supported + deprecated: < 24.9.0 is no longer supported hasBin: true qs@6.13.0: @@ -4858,6 +4995,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + send@0.19.0: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} @@ -4900,6 +5042,10 @@ packages: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -5718,13 +5864,6 @@ packages: snapshots: - '@ai-sdk/anthropic@2.0.34(zod@4.1.12)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.12(zod@4.1.12) - zod: 4.1.12 - optional: true - '@ai-sdk/anthropic@2.0.53(zod@4.1.12)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -6097,7 +6236,7 @@ snapshots: human-id: 4.1.1 prettier: 2.8.8 - '@emnapi/runtime@1.4.3': + '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 optional: true @@ -6346,81 +6485,177 @@ snapshots: '@humanwhocodes/retry@0.4.2': {} + '@img/colour@1.0.0': {} + '@img/sharp-darwin-arm64@0.33.5': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.0.4 optional: true + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + '@img/sharp-darwin-x64@0.33.5': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.0.4 optional: true + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + '@img/sharp-libvips-darwin-arm64@1.0.4': optional: true + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + '@img/sharp-libvips-darwin-x64@1.0.4': optional: true + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + '@img/sharp-libvips-linux-arm64@1.0.4': optional: true + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + '@img/sharp-libvips-linux-arm@1.0.5': optional: true + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + '@img/sharp-libvips-linux-s390x@1.0.4': optional: true + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + '@img/sharp-libvips-linux-x64@1.0.4': optional: true + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + '@img/sharp-libvips-linuxmusl-x64@1.0.4': optional: true + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + '@img/sharp-linux-arm64@0.33.5': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.0.4 optional: true + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + '@img/sharp-linux-arm@0.33.5': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.0.5 optional: true + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + '@img/sharp-linux-s390x@0.33.5': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.0.4 optional: true + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + '@img/sharp-linux-x64@0.33.5': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.0.4 optional: true + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + '@img/sharp-linuxmusl-arm64@0.33.5': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 optional: true + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + '@img/sharp-linuxmusl-x64@0.33.5': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.0.4 optional: true + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + '@img/sharp-wasm32@0.33.5': dependencies: - '@emnapi/runtime': 1.4.3 + '@emnapi/runtime': 1.7.1 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.7.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': optional: true '@img/sharp-win32-ia32@0.33.5': optional: true + '@img/sharp-win32-ia32@0.34.5': + optional: true + '@img/sharp-win32-x64@0.33.5': optional: true + '@img/sharp-win32-x64@0.34.5': + optional: true + '@inquirer/checkbox@4.1.5(@types/node@22.13.1)': dependencies: '@inquirer/core': 10.1.10(@types/node@22.13.1) @@ -7460,7 +7695,7 @@ snapshots: fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.7.1 + semver: 7.7.3 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: @@ -8164,7 +8399,7 @@ snapshots: detect-indent@6.1.0: {} - detect-libc@2.0.4: {} + detect-libc@2.1.2: {} detect-port@1.6.1: dependencies: @@ -11360,6 +11595,8 @@ snapshots: semver@7.7.2: {} + semver@7.7.3: {} + send@0.19.0: dependencies: debug: 2.6.9 @@ -11445,8 +11682,8 @@ snapshots: sharp@0.33.5: dependencies: color: 4.2.3 - detect-libc: 2.0.4 - semver: 7.7.2 + detect-libc: 2.1.2 + semver: 7.7.3 optionalDependencies: '@img/sharp-darwin-arm64': 0.33.5 '@img/sharp-darwin-x64': 0.33.5 @@ -11468,6 +11705,37 @@ snapshots: '@img/sharp-win32-ia32': 0.33.5 '@img/sharp-win32-x64': 0.33.5 + sharp@0.34.5: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0