├── .cursorignore ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── src └── index.ts ├── test_cat_image.png ├── test_cat_image_edited.png ├── test_cat_with_moustache.png ├── tsconfig.json └── yarn.lock /.cursorignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env 3 | /dist 4 | .DS_Store 5 | /dist 6 | /build 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openai-gpt-image-mcp 2 | 3 |

4 | MCP SDK 5 | OpenAI SDK 6 | License 7 | GitHub stars 8 | Build Status 9 |

10 | 11 | --- 12 | 13 | A Model Context Protocol (MCP) tool server for OpenAI's GPT-4o/gpt-image-1 image generation and editing APIs. 14 | 15 | - **Generate images** from text prompts using OpenAI's latest models. 16 | - **Edit images** (inpainting, outpainting, compositing) with advanced prompt control. 17 | - **Supports**: Claude Desktop, Cursor, VSCode, Windsurf, and any MCP-compatible client. 18 | 19 | --- 20 | 21 | ## ✨ Features 22 | 23 | - **create-image**: Generate images from a prompt, with advanced options (size, quality, background, etc). 24 | - **edit-image**: Edit or extend images using a prompt and optional mask, supporting both file paths and base64 input. 25 | - **File output**: Save generated images directly to disk, or receive as base64. 26 | 27 | --- 28 | 29 | ## 🚀 Installation 30 | 31 | ```sh 32 | git clone https://github.com/SureScaleAI/openai-gpt-image-mcp.git 33 | cd openai-gpt-image-mcp 34 | yarn install 35 | yarn build 36 | ``` 37 | 38 | --- 39 | 40 | ## 🔑 Configuration 41 | 42 | Add to Claude Desktop or VSCode (including Cursor/Windsurf) config: 43 | 44 | ```json 45 | { 46 | "mcpServers": { 47 | "openai-gpt-image-mcp": { 48 | "command": "node", 49 | "args": ["/absolute/path/to/dist/index.js"], 50 | "env": { "OPENAI_API_KEY": "sk-..." } 51 | } 52 | } 53 | } 54 | ``` 55 | 56 | Also supports Azure deployments: 57 | 58 | ```json 59 | { 60 | "mcpServers": { 61 | "openai-gpt-image-mcp": { 62 | "command": "node", 63 | "args": ["/absolute/path/to/dist/index.js"], 64 | "env": { 65 | "AZURE_OPENAI_API_KEY": "sk-...", 66 | "AZURE_OPENAI_ENDPOINT": "my.endpoint.com", 67 | "OPENAI_API_VERSION": "2024-12-01-preview" 68 | } 69 | } 70 | } 71 | } 72 | ``` 73 | 74 | Also supports supplying an environment files: 75 | 76 | ```json 77 | { 78 | "mcpServers": { 79 | "openai-gpt-image-mcp": { 80 | "command": "node", 81 | "args": ["/absolute/path/to/dist/index.js", "--env-file", "./deployment/.env"] 82 | } 83 | } 84 | } 85 | ``` 86 | 87 | --- 88 | 89 | ## ⚡ Advanced 90 | 91 | - For `create-image`, set `n` to generate up to 10 images at once. 92 | - For `edit-image`, provide a mask image (file path or base64) to control where edits are applied. 93 | - Provide an environment file with `--env-file path/to/file/.env` 94 | - See `src/index.ts` for all options. 95 | 96 | --- 97 | 98 | ## 🧑‍💻 Development 99 | 100 | - TypeScript source: `src/index.ts` 101 | - Build: `yarn build` 102 | - Run: `node dist/index.js` 103 | 104 | --- 105 | 106 | ## 📝 License 107 | 108 | MIT 109 | 110 | --- 111 | 112 | ## 🩺 Troubleshooting 113 | 114 | - Make sure your `OPENAI_API_KEY` is valid and has image API access. 115 | - You must have a [verified OpenAI organization](https://platform.openai.com/account/organization). After verifying, it can take 15–20 minutes for image API access to activate. 116 | - File paths must be absolute. 117 | - **Unix/macOS/Linux**: Starting with `/` (e.g., `/path/to/image.png`) 118 | - **Windows**: Drive letter followed by `:` (e.g., `C:/path/to/image.png` or `C:\path\to\image.png`) 119 | - For file output, ensure the directory is writable. 120 | - If you see errors about file types, check your image file extensions and formats. 121 | 122 | --- 123 | 124 | ## ⚠️ Limitations & Large File Handling 125 | 126 | - **1MB Payload Limit:** MCP clients (including Claude Desktop) have a hard 1MB limit for tool responses. Large images (especially high-res or multiple images) can easily exceed this limit if returned as base64. 127 | - **Auto-Switch to File Output:** If the total image size exceeds 1MB, the tool will automatically save images to disk and return the file path(s) instead of base64. This ensures compatibility and prevents errors like `result exceeds maximum length of 1048576`. 128 | - **Default File Location:** If you do not specify a `file_output` path, images will be saved to `/tmp` (or the directory set by the `MCP_HF_WORK_DIR` environment variable) with a unique filename. 129 | - **Environment Variable:** 130 | - `MCP_HF_WORK_DIR`: Set this to control where large images and file outputs are saved. Example: `export MCP_HF_WORK_DIR=/your/desired/dir` 131 | - **Best Practice:** For large or production images, always use file output and ensure your client is configured to handle file paths. 132 | 133 | --- 134 | 135 | ## 📚 References 136 | 137 | - [OpenAI Images API Documentation](https://platform.openai.com/docs/api-reference/images) 138 | 139 | --- 140 | 141 | ## 🙏 Credits 142 | 143 | - Built with [@modelcontextprotocol/sdk](https://www.npmjs.com/package/@modelcontextprotocol/sdk) 144 | - Uses [openai](https://www.npmjs.com/package/openai) Node.js SDK 145 | - Built by [SureScale.ai](https://surescale.ai) 146 | - Contributions from [Axle Research and Technology](https://axleinfo.com/) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openai-gpt-image-mcp", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "devDependencies": { 7 | "@types/node": "^22.15.2", 8 | "typescript": "^5.8.3" 9 | }, 10 | "dependencies": { 11 | "@modelcontextprotocol/sdk": "^1.10.2", 12 | "openai": "^4.96.0" 13 | }, 14 | "scripts": { 15 | "build": "tsc" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Suppress all Node.js warnings (including deprecation) 2 | (process as any).emitWarning = () => { }; 3 | 4 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 5 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 6 | import { z } from "zod"; 7 | import { OpenAI, AzureOpenAI, toFile } from "openai"; 8 | import fs from "fs"; 9 | import path from "path"; 10 | 11 | // Function to load environment variables from a file 12 | const loadEnvFile = (filePath: string) => { 13 | try { 14 | const envConfig = fs.readFileSync(filePath, "utf8"); 15 | envConfig.split("\n").forEach((line) => { 16 | const trimmedLine = line.trim(); 17 | if (trimmedLine && !trimmedLine.startsWith("#")) { 18 | const [key, ...valueParts] = trimmedLine.split("="); 19 | const value = valueParts.join("=").trim(); 20 | if (key) { 21 | // Remove surrounding quotes if present 22 | process.env[key.trim()] = value.startsWith("'") && value.endsWith("'") || value.startsWith("\"") && value.endsWith("\"") 23 | ? value.slice(1, -1) 24 | : value; 25 | } 26 | } 27 | }); 28 | console.log(`Loaded environment variables from ${filePath}`); 29 | } catch (error) { 30 | console.warn(`Warning: Could not read environment file at ${filePath}:`, error); 31 | } 32 | }; 33 | 34 | // Parse command line arguments for --env-file 35 | const cmdArgs = process.argv.slice(2); 36 | const envFileArgIndex = cmdArgs.findIndex(arg => arg === "--env-file"); 37 | if (envFileArgIndex !== -1 && cmdArgs[envFileArgIndex + 1]) { 38 | console.log("Loading environment variables from file:", cmdArgs[envFileArgIndex + 1]); 39 | const envFilePath = cmdArgs[envFileArgIndex + 1]; 40 | loadEnvFile(envFilePath); 41 | } else { 42 | console.log("No environment file provided"); 43 | } 44 | 45 | (async () => { 46 | const server = new McpServer({ 47 | name: "openai-gpt-image-mcp", 48 | version: "1.0.0" 49 | }, { 50 | capabilities: { 51 | tools: { listChanged: false } 52 | } 53 | }); 54 | 55 | // Zod schema for create-image tool input 56 | const createImageSchema = z.object({ 57 | prompt: z.string().max(32000), 58 | background: z.enum(["transparent", "opaque", "auto"]).optional(), 59 | model: z.literal("gpt-image-1").default("gpt-image-1"), 60 | moderation: z.enum(["auto", "low"]).optional(), 61 | n: z.number().int().min(1).max(10).optional(), 62 | output_compression: z.number().int().min(0).max(100).optional(), 63 | output_format: z.enum(["png", "jpeg", "webp"]).optional(), 64 | quality: z.enum(["auto", "high", "medium", "low"]).optional(), 65 | size: z.enum(["1024x1024", "1536x1024", "1024x1536", "auto"]).optional(), 66 | user: z.string().optional(), 67 | output: z.enum(["base64", "file_output"]).default("base64"), 68 | file_output: z.string().optional().refine( 69 | (val) => { 70 | if (!val) return true; 71 | // Check for Unix/Linux/macOS absolute paths 72 | if (val.startsWith("/")) return true; 73 | // Check for Windows absolute paths (C:/, D:\, etc.) 74 | if (/^[a-zA-Z]:[/\\]/.test(val)) return true; 75 | return false; 76 | }, 77 | { message: "file_output must be an absolute path" } 78 | ).describe("Absolute path to save the image file, including the desired file extension (e.g., /path/to/image.png). If multiple images are generated (n > 1), an index will be appended (e.g., /path/to/image_1.png)."), 79 | }).refine( 80 | (data) => { 81 | if (data.output !== "file_output") return true; 82 | if (typeof data.file_output !== "string") return false; 83 | // Check for Unix/Linux/macOS absolute paths 84 | if (data.file_output.startsWith("/")) return true; 85 | // Check for Windows absolute paths (C:/, D:\, etc.) 86 | if (/^[a-zA-Z]:[/\\]/.test(data.file_output)) return true; 87 | return false; 88 | }, 89 | { message: "file_output must be an absolute path when output is 'file_output'", path: ["file_output"] } 90 | ); 91 | 92 | // Use ._def.schema.shape to get the raw shape for server.tool due to Zod refinements 93 | server.tool( 94 | "create-image", 95 | (createImageSchema as any)._def.schema.shape, 96 | async (args, _extra) => { 97 | // If AZURE_OPENAI_API_KEY is defined, use the AzureOpenAI class 98 | const openai = process.env.AZURE_OPENAI_API_KEY ? new AzureOpenAI() : new OpenAI(); 99 | 100 | // Only allow gpt-image-1 101 | const { 102 | prompt, 103 | background, 104 | model = "gpt-image-1", 105 | moderation, 106 | n, 107 | output_compression, 108 | output_format, 109 | quality, 110 | size, 111 | user, 112 | output = "base64", 113 | file_output: file_outputRaw, 114 | } = args; 115 | const file_output: string | undefined = file_outputRaw; 116 | 117 | // Enforce: if background is 'transparent', output_format must be 'png' or 'webp' 118 | if (background === "transparent" && output_format && !["png", "webp"].includes(output_format)) { 119 | throw new Error("If background is 'transparent', output_format must be 'png' or 'webp'"); 120 | } 121 | 122 | // Only include output_compression if output_format is webp or jpeg 123 | const imageParams: any = { 124 | prompt, 125 | model, 126 | ...(background ? { background } : {}), 127 | ...(moderation ? { moderation } : {}), 128 | ...(n ? { n } : {}), 129 | ...(output_format ? { output_format } : {}), 130 | ...(quality ? { quality } : {}), 131 | ...(size ? { size } : {}), 132 | ...(user ? { user } : {}), 133 | }; 134 | if ( 135 | typeof output_compression !== "undefined" && 136 | output_format && 137 | ["webp", "jpeg"].includes(output_format) 138 | ) { 139 | imageParams.output_compression = output_compression; 140 | } 141 | 142 | const result = await openai.images.generate(imageParams); 143 | 144 | // gpt-image-1 always returns base64 images in data[].b64_json 145 | const images = (result.data ?? []).map((img: any) => ({ 146 | b64: img.b64_json, 147 | mimeType: output_format === "jpeg" ? "image/jpeg" : output_format === "webp" ? "image/webp" : "image/png", 148 | ext: output_format === "jpeg" ? "jpg" : output_format === "webp" ? "webp" : "png", 149 | })); 150 | 151 | // Auto-switch to file_output if total base64 size exceeds 1MB 152 | const MAX_RESPONSE_SIZE = 1048576; // 1MB 153 | const totalBase64Size = images.reduce((sum, img) => sum + Buffer.byteLength(img.b64, "base64"), 0); 154 | let effectiveOutput = output; 155 | let effectiveFileOutput = file_output; 156 | if (output === "base64" && totalBase64Size > MAX_RESPONSE_SIZE) { 157 | effectiveOutput = "file_output"; 158 | if (!file_output) { 159 | // Use /tmp or MCP_HF_WORK_DIR if set 160 | const tmpDir = process.env.MCP_HF_WORK_DIR || "/tmp"; 161 | const unique = Date.now(); 162 | effectiveFileOutput = path.join(tmpDir, `openai_image_${unique}.${images[0]?.ext ?? "png"}`); 163 | } 164 | } 165 | 166 | if (effectiveOutput === "file_output") { 167 | const fs = await import("fs/promises"); 168 | const path = await import("path"); 169 | // If multiple images, append index to filename 170 | const basePath = effectiveFileOutput!; 171 | const responses = []; 172 | for (let i = 0; i < images.length; i++) { 173 | const img = images[i]; 174 | let filePath = basePath; 175 | if (images.length > 1) { 176 | const parsed = path.parse(basePath); 177 | filePath = path.join(parsed.dir, `${parsed.name}_${i + 1}.${img.ext ?? "png"}`); 178 | } else { 179 | // Ensure correct extension 180 | const parsed = path.parse(basePath); 181 | filePath = path.join(parsed.dir, `${parsed.name}.${img.ext ?? "png"}`); 182 | } 183 | await fs.writeFile(filePath, Buffer.from(img.b64, "base64")); 184 | responses.push({ type: "text", text: `Image saved to: file://${filePath}` }); 185 | } 186 | return { content: responses }; 187 | } else { 188 | // Default: base64 189 | return { 190 | content: images.map((img) => ({ 191 | type: "image", 192 | data: img.b64, 193 | mimeType: img.mimeType, 194 | })), 195 | }; 196 | } 197 | } 198 | ); 199 | 200 | // Zod schema for edit-image tool input (gpt-image-1 only) 201 | const absolutePathCheck = (val: string | undefined) => { 202 | if (!val) return true; 203 | // Check for Unix/Linux/macOS absolute paths 204 | if (val.startsWith("/")) return true; 205 | // Check for Windows absolute paths (C:/, D:\, etc.) 206 | if (/^[a-zA-Z]:[/\\]/.test(val)) return true; 207 | return false; 208 | }; 209 | const base64Check = (val: string | undefined) => !!val && (/^([A-Za-z0-9+/=\r\n]+)$/.test(val) || val.startsWith("data:image/")); 210 | const imageInputSchema = z.string().refine( 211 | (val) => absolutePathCheck(val) || base64Check(val), 212 | { message: "Must be an absolute path or a base64-encoded string (optionally as a data URL)" } 213 | ).describe("Absolute path to an image file (png, jpg, webp < 25MB) or a base64-encoded image string."); 214 | 215 | // Base schema without refinement for server.tool signature 216 | const editImageBaseSchema = z.object({ 217 | image: z.string().describe("Absolute image path or base64 string to edit."), 218 | prompt: z.string().max(32000).describe("A text description of the desired edit. Max 32000 chars."), 219 | mask: z.string().optional().describe("Optional absolute path or base64 string for a mask image (png < 4MB, same dimensions as the first image). Fully transparent areas indicate where to edit."), 220 | model: z.literal("gpt-image-1").default("gpt-image-1"), 221 | n: z.number().int().min(1).max(10).optional().describe("Number of images to generate (1-10)."), 222 | quality: z.enum(["auto", "high", "medium", "low"]).optional().describe("Quality (high, medium, low) - only for gpt-image-1."), 223 | size: z.enum(["1024x1024", "1536x1024", "1024x1536", "auto"]).optional().describe("Size of the generated images."), 224 | user: z.string().optional().describe("Optional user identifier for OpenAI monitoring."), 225 | output: z.enum(["base64", "file_output"]).default("base64").describe("Output format: base64 or file path."), 226 | file_output: z.string().refine(absolutePathCheck, { message: "Path must be absolute" }).optional() 227 | .describe("Absolute path to save the output image file, including the desired file extension (e.g., /path/to/image.png). If n > 1, an index is appended."), 228 | }); 229 | 230 | // Full schema with refinement for validation inside the handler 231 | const editImageSchema = editImageBaseSchema.refine( 232 | (data) => { 233 | if (data.output !== "file_output") return true; 234 | if (typeof data.file_output !== "string") return false; 235 | return absolutePathCheck(data.file_output); 236 | }, 237 | { message: "file_output must be an absolute path when output is 'file_output'", path: ["file_output"] } 238 | ); 239 | 240 | // Edit Image Tool (gpt-image-1 only) 241 | server.tool( 242 | "edit-image", 243 | editImageBaseSchema.shape, // <-- Use the base schema shape here 244 | async (args, _extra) => { 245 | // Validate arguments using the full schema with refinements 246 | const validatedArgs = editImageSchema.parse(args); 247 | 248 | // Explicitly validate image and mask inputs here 249 | if (!absolutePathCheck(validatedArgs.image) && !base64Check(validatedArgs.image)) { 250 | throw new Error("Invalid 'image' input: Must be an absolute path or a base64-encoded string."); 251 | } 252 | if (validatedArgs.mask && !absolutePathCheck(validatedArgs.mask) && !base64Check(validatedArgs.mask)) { 253 | throw new Error("Invalid 'mask' input: Must be an absolute path or a base64-encoded string."); 254 | } 255 | 256 | const openai = process.env.AZURE_OPENAI_API_KEY ? new AzureOpenAI() : new OpenAI(); 257 | const { 258 | image: imageInput, 259 | prompt, 260 | mask: maskInput, 261 | model = "gpt-image-1", 262 | n, 263 | quality, 264 | size, 265 | user, 266 | output = "base64", 267 | file_output: file_outputRaw, 268 | } = validatedArgs; // <-- Use validatedArgs here 269 | const file_output: string | undefined = file_outputRaw; 270 | 271 | // Helper to convert input (path or base64) to toFile 272 | async function inputToFile(input: string, idx = 0) { 273 | if (absolutePathCheck(input)) { 274 | // File path: infer mime type from extension 275 | const ext = input.split('.').pop()?.toLowerCase(); 276 | let mime = "image/png"; 277 | if (ext === "jpg" || ext === "jpeg") mime = "image/jpeg"; 278 | else if (ext === "webp") mime = "image/webp"; 279 | else if (ext === "png") mime = "image/png"; 280 | // else default to png 281 | return await toFile(fs.createReadStream(input), undefined, { type: mime }); 282 | } else { 283 | // Base64 or data URL 284 | let base64 = input; 285 | let mime = "image/png"; 286 | if (input.startsWith("data:image/")) { 287 | // data URL 288 | const match = input.match(/^data:(image\/\w+);base64,(.*)$/); 289 | if (match) { 290 | mime = match[1]; 291 | base64 = match[2]; 292 | } 293 | } 294 | const buffer = Buffer.from(base64, "base64"); 295 | return await toFile(buffer, `input_${idx}.${mime.split("/")[1] || "png"}`, { type: mime }); 296 | } 297 | } 298 | 299 | // Prepare image input 300 | const imageFile = await inputToFile(imageInput, 0); 301 | 302 | // Prepare mask input 303 | const maskFile = maskInput ? await inputToFile(maskInput, 1) : undefined; 304 | 305 | // Construct parameters for OpenAI API 306 | const editParams: any = { 307 | image: imageFile, 308 | prompt, 309 | model, // Always gpt-image-1 310 | ...(maskFile ? { mask: maskFile } : {}), 311 | ...(n ? { n } : {}), 312 | ...(quality ? { quality } : {}), 313 | ...(size ? { size } : {}), 314 | ...(user ? { user } : {}), 315 | // response_format is not applicable for gpt-image-1 (always b64_json) 316 | }; 317 | 318 | const result = await openai.images.edit(editParams); 319 | 320 | // gpt-image-1 always returns base64 images in data[].b64_json 321 | // We need to determine the output mime type and extension based on input/defaults 322 | // Since OpenAI doesn't return this for edits, we'll default to png 323 | const images = (result.data ?? []).map((img: any) => ({ 324 | b64: img.b64_json, 325 | mimeType: "image/png", 326 | ext: "png", 327 | })); 328 | 329 | // Auto-switch to file_output if total base64 size exceeds 1MB 330 | const MAX_RESPONSE_SIZE = 1048576; // 1MB 331 | const totalBase64Size = images.reduce((sum, img) => sum + Buffer.byteLength(img.b64, "base64"), 0); 332 | let effectiveOutput = output; 333 | let effectiveFileOutput = file_output; 334 | if (output === "base64" && totalBase64Size > MAX_RESPONSE_SIZE) { 335 | effectiveOutput = "file_output"; 336 | if (!file_output) { 337 | // Use /tmp or MCP_HF_WORK_DIR if set 338 | const tmpDir = process.env.MCP_HF_WORK_DIR || "/tmp"; 339 | const unique = Date.now(); 340 | effectiveFileOutput = path.join(tmpDir, `openai_image_edit_${unique}.png`); 341 | } 342 | } 343 | 344 | if (effectiveOutput === "file_output") { 345 | if (!effectiveFileOutput) { 346 | throw new Error("file_output path is required when output is 'file_output'"); 347 | } 348 | // Use fs/promises and path (already imported) 349 | const basePath = effectiveFileOutput!; 350 | const responses = []; 351 | for (let i = 0; i < images.length; i++) { 352 | const img = images[i]; 353 | let filePath = basePath; 354 | if (images.length > 1) { 355 | const parsed = path.parse(basePath); 356 | // Append index before the original extension if it exists, otherwise just append index and .png 357 | const ext = parsed.ext || `.${img.ext}`; 358 | filePath = path.join(parsed.dir, `${parsed.name}_${i + 1}${ext}`); 359 | } else { 360 | // Ensure the extension from the path is used, or default to .png 361 | const parsed = path.parse(basePath); 362 | const ext = parsed.ext || `.${img.ext}`; 363 | filePath = path.join(parsed.dir, `${parsed.name}${ext}`); 364 | } 365 | await fs.promises.writeFile(filePath, Buffer.from(img.b64, "base64")); 366 | // Workaround: Return file path as text 367 | responses.push({ type: "text", text: `Image saved to: file://${filePath}` }); 368 | } 369 | return { content: responses }; 370 | } else { 371 | // Default: base64 372 | return { 373 | content: images.map((img) => ({ 374 | type: "image", 375 | data: img.b64, 376 | mimeType: img.mimeType, // Should be image/png 377 | })), 378 | }; 379 | } 380 | } 381 | ); 382 | 383 | const transport = new StdioServerTransport(); 384 | await server.connect(transport); 385 | })(); -------------------------------------------------------------------------------- /test_cat_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SureScaleAI/openai-gpt-image-mcp/558265a1c92d4ab51efdd03135286b727487204c/test_cat_image.png -------------------------------------------------------------------------------- /test_cat_image_edited.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SureScaleAI/openai-gpt-image-mcp/558265a1c92d4ab51efdd03135286b727487204c/test_cat_image_edited.png -------------------------------------------------------------------------------- /test_cat_with_moustache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SureScaleAI/openai-gpt-image-mcp/558265a1c92d4ab51efdd03135286b727487204c/test_cat_with_moustache.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "libReplacement": true, /* Enable lib replacement. */ 18 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 19 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 20 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 21 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 22 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 23 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 24 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 25 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 26 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 27 | 28 | /* Modules */ 29 | "module": "commonjs", /* Specify what module code is generated. */ 30 | "rootDir": "src", /* Specify the root folder within your source files. */ 31 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 32 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 33 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 34 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 35 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 36 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 37 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 38 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 39 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 40 | // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ 41 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 42 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 43 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 44 | // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ 45 | // "resolveJsonModule": true, /* Enable importing .json files. */ 46 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 47 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 48 | 49 | /* JavaScript Support */ 50 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 51 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 52 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 53 | 54 | /* Emit */ 55 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 56 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 57 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 58 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 59 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 62 | "outDir": "dist", /* Specify an output folder for all emitted files. */ 63 | // "removeComments": true, /* Disable emitting comments. */ 64 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 65 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 66 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 67 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 68 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 69 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 70 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 71 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 72 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 73 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 74 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 75 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 76 | 77 | /* Interop Constraints */ 78 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 79 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 80 | // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ 81 | // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ 82 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 83 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 84 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 85 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 86 | 87 | /* Type Checking */ 88 | "strict": false, /* Enable all strict type-checking options. */ 89 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 90 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 91 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 92 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 93 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 94 | // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ 95 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 96 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 97 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 98 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 99 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 100 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 101 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 102 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 103 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 104 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 105 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 106 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 107 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 108 | 109 | /* Completeness */ 110 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 111 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@modelcontextprotocol/sdk@^1.10.2": 6 | version "1.10.2" 7 | resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.10.2.tgz#50cdfbf0b6fbea23420388a7b00e64c13adabac8" 8 | integrity sha512-rb6AMp2DR4SN+kc6L1ta2NCpApyA9WYNx3CrTSZvGxq9wH71bRur+zRqPfg0vQ9mjywR7qZdX2RGHOPq3ss+tA== 9 | dependencies: 10 | content-type "^1.0.5" 11 | cors "^2.8.5" 12 | cross-spawn "^7.0.3" 13 | eventsource "^3.0.2" 14 | express "^5.0.1" 15 | express-rate-limit "^7.5.0" 16 | pkce-challenge "^5.0.0" 17 | raw-body "^3.0.0" 18 | zod "^3.23.8" 19 | zod-to-json-schema "^3.24.1" 20 | 21 | "@types/node-fetch@^2.6.4": 22 | version "2.6.12" 23 | resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03" 24 | integrity sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA== 25 | dependencies: 26 | "@types/node" "*" 27 | form-data "^4.0.0" 28 | 29 | "@types/node@*", "@types/node@^22.15.2": 30 | version "22.15.2" 31 | resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.2.tgz#1db55aa64618ee93a58c8912f74beefe44aca905" 32 | integrity sha512-uKXqKN9beGoMdBfcaTY1ecwz6ctxuJAcUlwE55938g0ZJ8lRxwAZqRz2AJ4pzpt5dHdTPMB863UZ0ESiFUcP7A== 33 | dependencies: 34 | undici-types "~6.21.0" 35 | 36 | "@types/node@^18.11.18": 37 | version "18.19.87" 38 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.87.tgz#690f000cc51e3c7f48bc00f7e86fac6eb550b709" 39 | integrity sha512-OIAAu6ypnVZHmsHCeJ+7CCSub38QNBS9uceMQeg7K5Ur0Jr+wG9wEOEvvMbhp09pxD5czIUy/jND7s7Tb6Nw7A== 40 | dependencies: 41 | undici-types "~5.26.4" 42 | 43 | abort-controller@^3.0.0: 44 | version "3.0.0" 45 | resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" 46 | integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== 47 | dependencies: 48 | event-target-shim "^5.0.0" 49 | 50 | accepts@^2.0.0: 51 | version "2.0.0" 52 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" 53 | integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== 54 | dependencies: 55 | mime-types "^3.0.0" 56 | negotiator "^1.0.0" 57 | 58 | agentkeepalive@^4.2.1: 59 | version "4.6.0" 60 | resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" 61 | integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ== 62 | dependencies: 63 | humanize-ms "^1.2.1" 64 | 65 | asynckit@^0.4.0: 66 | version "0.4.0" 67 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 68 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== 69 | 70 | body-parser@^2.2.0: 71 | version "2.2.0" 72 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.0.tgz#f7a9656de305249a715b549b7b8fd1ab9dfddcfa" 73 | integrity sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg== 74 | dependencies: 75 | bytes "^3.1.2" 76 | content-type "^1.0.5" 77 | debug "^4.4.0" 78 | http-errors "^2.0.0" 79 | iconv-lite "^0.6.3" 80 | on-finished "^2.4.1" 81 | qs "^6.14.0" 82 | raw-body "^3.0.0" 83 | type-is "^2.0.0" 84 | 85 | bytes@3.1.2, bytes@^3.1.2: 86 | version "3.1.2" 87 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" 88 | integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== 89 | 90 | call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: 91 | version "1.0.2" 92 | resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" 93 | integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== 94 | dependencies: 95 | es-errors "^1.3.0" 96 | function-bind "^1.1.2" 97 | 98 | call-bound@^1.0.2: 99 | version "1.0.4" 100 | resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" 101 | integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== 102 | dependencies: 103 | call-bind-apply-helpers "^1.0.2" 104 | get-intrinsic "^1.3.0" 105 | 106 | combined-stream@^1.0.8: 107 | version "1.0.8" 108 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 109 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 110 | dependencies: 111 | delayed-stream "~1.0.0" 112 | 113 | content-disposition@^1.0.0: 114 | version "1.0.0" 115 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.0.tgz#844426cb398f934caefcbb172200126bc7ceace2" 116 | integrity sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg== 117 | dependencies: 118 | safe-buffer "5.2.1" 119 | 120 | content-type@^1.0.5: 121 | version "1.0.5" 122 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" 123 | integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== 124 | 125 | cookie-signature@^1.2.1: 126 | version "1.2.2" 127 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" 128 | integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== 129 | 130 | cookie@^0.7.1: 131 | version "0.7.2" 132 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" 133 | integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== 134 | 135 | cors@^2.8.5: 136 | version "2.8.5" 137 | resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" 138 | integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== 139 | dependencies: 140 | object-assign "^4" 141 | vary "^1" 142 | 143 | cross-spawn@^7.0.3: 144 | version "7.0.6" 145 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" 146 | integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== 147 | dependencies: 148 | path-key "^3.1.0" 149 | shebang-command "^2.0.0" 150 | which "^2.0.1" 151 | 152 | debug@^4.3.5, debug@^4.4.0: 153 | version "4.4.0" 154 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" 155 | integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== 156 | dependencies: 157 | ms "^2.1.3" 158 | 159 | delayed-stream@~1.0.0: 160 | version "1.0.0" 161 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 162 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== 163 | 164 | depd@2.0.0, depd@^2.0.0: 165 | version "2.0.0" 166 | resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" 167 | integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== 168 | 169 | dunder-proto@^1.0.1: 170 | version "1.0.1" 171 | resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" 172 | integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== 173 | dependencies: 174 | call-bind-apply-helpers "^1.0.1" 175 | es-errors "^1.3.0" 176 | gopd "^1.2.0" 177 | 178 | ee-first@1.1.1: 179 | version "1.1.1" 180 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 181 | integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== 182 | 183 | encodeurl@^2.0.0: 184 | version "2.0.0" 185 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" 186 | integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== 187 | 188 | es-define-property@^1.0.1: 189 | version "1.0.1" 190 | resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" 191 | integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== 192 | 193 | es-errors@^1.3.0: 194 | version "1.3.0" 195 | resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" 196 | integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== 197 | 198 | es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: 199 | version "1.1.1" 200 | resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" 201 | integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== 202 | dependencies: 203 | es-errors "^1.3.0" 204 | 205 | es-set-tostringtag@^2.1.0: 206 | version "2.1.0" 207 | resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" 208 | integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== 209 | dependencies: 210 | es-errors "^1.3.0" 211 | get-intrinsic "^1.2.6" 212 | has-tostringtag "^1.0.2" 213 | hasown "^2.0.2" 214 | 215 | escape-html@^1.0.3: 216 | version "1.0.3" 217 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 218 | integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== 219 | 220 | etag@^1.8.1: 221 | version "1.8.1" 222 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 223 | integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== 224 | 225 | event-target-shim@^5.0.0: 226 | version "5.0.1" 227 | resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" 228 | integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== 229 | 230 | eventsource-parser@^3.0.1: 231 | version "3.0.1" 232 | resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.1.tgz#5e358dba9a55ba64ca90da883c4ca35bd82467bd" 233 | integrity sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA== 234 | 235 | eventsource@^3.0.2: 236 | version "3.0.6" 237 | resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-3.0.6.tgz#5c4b24cd70c0323eed2651a5ee07bd4bc391e656" 238 | integrity sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA== 239 | dependencies: 240 | eventsource-parser "^3.0.1" 241 | 242 | express-rate-limit@^7.5.0: 243 | version "7.5.0" 244 | resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.5.0.tgz#6a67990a724b4fbbc69119419feef50c51e8b28f" 245 | integrity sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg== 246 | 247 | express@^5.0.1: 248 | version "5.1.0" 249 | resolved "https://registry.yarnpkg.com/express/-/express-5.1.0.tgz#d31beaf715a0016f0d53f47d3b4d7acf28c75cc9" 250 | integrity sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA== 251 | dependencies: 252 | accepts "^2.0.0" 253 | body-parser "^2.2.0" 254 | content-disposition "^1.0.0" 255 | content-type "^1.0.5" 256 | cookie "^0.7.1" 257 | cookie-signature "^1.2.1" 258 | debug "^4.4.0" 259 | encodeurl "^2.0.0" 260 | escape-html "^1.0.3" 261 | etag "^1.8.1" 262 | finalhandler "^2.1.0" 263 | fresh "^2.0.0" 264 | http-errors "^2.0.0" 265 | merge-descriptors "^2.0.0" 266 | mime-types "^3.0.0" 267 | on-finished "^2.4.1" 268 | once "^1.4.0" 269 | parseurl "^1.3.3" 270 | proxy-addr "^2.0.7" 271 | qs "^6.14.0" 272 | range-parser "^1.2.1" 273 | router "^2.2.0" 274 | send "^1.1.0" 275 | serve-static "^2.2.0" 276 | statuses "^2.0.1" 277 | type-is "^2.0.1" 278 | vary "^1.1.2" 279 | 280 | finalhandler@^2.1.0: 281 | version "2.1.0" 282 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.0.tgz#72306373aa89d05a8242ed569ed86a1bff7c561f" 283 | integrity sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q== 284 | dependencies: 285 | debug "^4.4.0" 286 | encodeurl "^2.0.0" 287 | escape-html "^1.0.3" 288 | on-finished "^2.4.1" 289 | parseurl "^1.3.3" 290 | statuses "^2.0.1" 291 | 292 | form-data-encoder@1.7.2: 293 | version "1.7.2" 294 | resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" 295 | integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== 296 | 297 | form-data@^4.0.0: 298 | version "4.0.2" 299 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" 300 | integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== 301 | dependencies: 302 | asynckit "^0.4.0" 303 | combined-stream "^1.0.8" 304 | es-set-tostringtag "^2.1.0" 305 | mime-types "^2.1.12" 306 | 307 | formdata-node@^4.3.2: 308 | version "4.4.1" 309 | resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" 310 | integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== 311 | dependencies: 312 | node-domexception "1.0.0" 313 | web-streams-polyfill "4.0.0-beta.3" 314 | 315 | forwarded@0.2.0: 316 | version "0.2.0" 317 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" 318 | integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== 319 | 320 | fresh@^2.0.0: 321 | version "2.0.0" 322 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" 323 | integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== 324 | 325 | function-bind@^1.1.2: 326 | version "1.1.2" 327 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" 328 | integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== 329 | 330 | get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: 331 | version "1.3.0" 332 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" 333 | integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== 334 | dependencies: 335 | call-bind-apply-helpers "^1.0.2" 336 | es-define-property "^1.0.1" 337 | es-errors "^1.3.0" 338 | es-object-atoms "^1.1.1" 339 | function-bind "^1.1.2" 340 | get-proto "^1.0.1" 341 | gopd "^1.2.0" 342 | has-symbols "^1.1.0" 343 | hasown "^2.0.2" 344 | math-intrinsics "^1.1.0" 345 | 346 | get-proto@^1.0.1: 347 | version "1.0.1" 348 | resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" 349 | integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== 350 | dependencies: 351 | dunder-proto "^1.0.1" 352 | es-object-atoms "^1.0.0" 353 | 354 | gopd@^1.2.0: 355 | version "1.2.0" 356 | resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" 357 | integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== 358 | 359 | has-symbols@^1.0.3, has-symbols@^1.1.0: 360 | version "1.1.0" 361 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" 362 | integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== 363 | 364 | has-tostringtag@^1.0.2: 365 | version "1.0.2" 366 | resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" 367 | integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== 368 | dependencies: 369 | has-symbols "^1.0.3" 370 | 371 | hasown@^2.0.2: 372 | version "2.0.2" 373 | resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" 374 | integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== 375 | dependencies: 376 | function-bind "^1.1.2" 377 | 378 | http-errors@2.0.0, http-errors@^2.0.0: 379 | version "2.0.0" 380 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" 381 | integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== 382 | dependencies: 383 | depd "2.0.0" 384 | inherits "2.0.4" 385 | setprototypeof "1.2.0" 386 | statuses "2.0.1" 387 | toidentifier "1.0.1" 388 | 389 | humanize-ms@^1.2.1: 390 | version "1.2.1" 391 | resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" 392 | integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== 393 | dependencies: 394 | ms "^2.0.0" 395 | 396 | iconv-lite@0.6.3, iconv-lite@^0.6.3: 397 | version "0.6.3" 398 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" 399 | integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== 400 | dependencies: 401 | safer-buffer ">= 2.1.2 < 3.0.0" 402 | 403 | inherits@2.0.4: 404 | version "2.0.4" 405 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 406 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 407 | 408 | ipaddr.js@1.9.1: 409 | version "1.9.1" 410 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" 411 | integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== 412 | 413 | is-promise@^4.0.0: 414 | version "4.0.0" 415 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" 416 | integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== 417 | 418 | isexe@^2.0.0: 419 | version "2.0.0" 420 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 421 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== 422 | 423 | math-intrinsics@^1.1.0: 424 | version "1.1.0" 425 | resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" 426 | integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== 427 | 428 | media-typer@^1.1.0: 429 | version "1.1.0" 430 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" 431 | integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== 432 | 433 | merge-descriptors@^2.0.0: 434 | version "2.0.0" 435 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" 436 | integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== 437 | 438 | mime-db@1.52.0: 439 | version "1.52.0" 440 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" 441 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 442 | 443 | mime-db@^1.54.0: 444 | version "1.54.0" 445 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" 446 | integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== 447 | 448 | mime-types@^2.1.12: 449 | version "2.1.35" 450 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" 451 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 452 | dependencies: 453 | mime-db "1.52.0" 454 | 455 | mime-types@^3.0.0, mime-types@^3.0.1: 456 | version "3.0.1" 457 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce" 458 | integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA== 459 | dependencies: 460 | mime-db "^1.54.0" 461 | 462 | ms@^2.0.0, ms@^2.1.3: 463 | version "2.1.3" 464 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 465 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 466 | 467 | negotiator@^1.0.0: 468 | version "1.0.0" 469 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" 470 | integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== 471 | 472 | node-domexception@1.0.0: 473 | version "1.0.0" 474 | resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" 475 | integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== 476 | 477 | node-fetch@^2.6.7: 478 | version "2.7.0" 479 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" 480 | integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== 481 | dependencies: 482 | whatwg-url "^5.0.0" 483 | 484 | object-assign@^4: 485 | version "4.1.1" 486 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 487 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== 488 | 489 | object-inspect@^1.13.3: 490 | version "1.13.4" 491 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" 492 | integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== 493 | 494 | on-finished@^2.4.1: 495 | version "2.4.1" 496 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" 497 | integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== 498 | dependencies: 499 | ee-first "1.1.1" 500 | 501 | once@^1.4.0: 502 | version "1.4.0" 503 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 504 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 505 | dependencies: 506 | wrappy "1" 507 | 508 | openai@^4.96.0: 509 | version "4.96.0" 510 | resolved "https://registry.yarnpkg.com/openai/-/openai-4.96.0.tgz#d1a821e99949ac2c55709f4e28e18bb1d9fd8ef9" 511 | integrity sha512-dKoW56i02Prv2XQolJ9Rl9Svqubqkzg3QpwEOBuSVZLk05Shelu7s+ErRTwFc1Bs3JZ2qBqBfVpXQiJhwOGG8A== 512 | dependencies: 513 | "@types/node" "^18.11.18" 514 | "@types/node-fetch" "^2.6.4" 515 | abort-controller "^3.0.0" 516 | agentkeepalive "^4.2.1" 517 | form-data-encoder "1.7.2" 518 | formdata-node "^4.3.2" 519 | node-fetch "^2.6.7" 520 | 521 | parseurl@^1.3.3: 522 | version "1.3.3" 523 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" 524 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 525 | 526 | path-key@^3.1.0: 527 | version "3.1.1" 528 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 529 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 530 | 531 | path-to-regexp@^8.0.0: 532 | version "8.2.0" 533 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" 534 | integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ== 535 | 536 | pkce-challenge@^5.0.0: 537 | version "5.0.0" 538 | resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-5.0.0.tgz#c3a405cb49e272094a38e890a2b51da0228c4d97" 539 | integrity sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ== 540 | 541 | proxy-addr@^2.0.7: 542 | version "2.0.7" 543 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" 544 | integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== 545 | dependencies: 546 | forwarded "0.2.0" 547 | ipaddr.js "1.9.1" 548 | 549 | qs@^6.14.0: 550 | version "6.14.0" 551 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" 552 | integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== 553 | dependencies: 554 | side-channel "^1.1.0" 555 | 556 | range-parser@^1.2.1: 557 | version "1.2.1" 558 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" 559 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== 560 | 561 | raw-body@^3.0.0: 562 | version "3.0.0" 563 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f" 564 | integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g== 565 | dependencies: 566 | bytes "3.1.2" 567 | http-errors "2.0.0" 568 | iconv-lite "0.6.3" 569 | unpipe "1.0.0" 570 | 571 | router@^2.2.0: 572 | version "2.2.0" 573 | resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" 574 | integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== 575 | dependencies: 576 | debug "^4.4.0" 577 | depd "^2.0.0" 578 | is-promise "^4.0.0" 579 | parseurl "^1.3.3" 580 | path-to-regexp "^8.0.0" 581 | 582 | safe-buffer@5.2.1: 583 | version "5.2.1" 584 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 585 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 586 | 587 | "safer-buffer@>= 2.1.2 < 3.0.0": 588 | version "2.1.2" 589 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 590 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 591 | 592 | send@^1.1.0, send@^1.2.0: 593 | version "1.2.0" 594 | resolved "https://registry.yarnpkg.com/send/-/send-1.2.0.tgz#32a7554fb777b831dfa828370f773a3808d37212" 595 | integrity sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw== 596 | dependencies: 597 | debug "^4.3.5" 598 | encodeurl "^2.0.0" 599 | escape-html "^1.0.3" 600 | etag "^1.8.1" 601 | fresh "^2.0.0" 602 | http-errors "^2.0.0" 603 | mime-types "^3.0.1" 604 | ms "^2.1.3" 605 | on-finished "^2.4.1" 606 | range-parser "^1.2.1" 607 | statuses "^2.0.1" 608 | 609 | serve-static@^2.2.0: 610 | version "2.2.0" 611 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.0.tgz#9c02564ee259bdd2251b82d659a2e7e1938d66f9" 612 | integrity sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ== 613 | dependencies: 614 | encodeurl "^2.0.0" 615 | escape-html "^1.0.3" 616 | parseurl "^1.3.3" 617 | send "^1.2.0" 618 | 619 | setprototypeof@1.2.0: 620 | version "1.2.0" 621 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" 622 | integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== 623 | 624 | shebang-command@^2.0.0: 625 | version "2.0.0" 626 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 627 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 628 | dependencies: 629 | shebang-regex "^3.0.0" 630 | 631 | shebang-regex@^3.0.0: 632 | version "3.0.0" 633 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 634 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 635 | 636 | side-channel-list@^1.0.0: 637 | version "1.0.0" 638 | resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" 639 | integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== 640 | dependencies: 641 | es-errors "^1.3.0" 642 | object-inspect "^1.13.3" 643 | 644 | side-channel-map@^1.0.1: 645 | version "1.0.1" 646 | resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" 647 | integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== 648 | dependencies: 649 | call-bound "^1.0.2" 650 | es-errors "^1.3.0" 651 | get-intrinsic "^1.2.5" 652 | object-inspect "^1.13.3" 653 | 654 | side-channel-weakmap@^1.0.2: 655 | version "1.0.2" 656 | resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" 657 | integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== 658 | dependencies: 659 | call-bound "^1.0.2" 660 | es-errors "^1.3.0" 661 | get-intrinsic "^1.2.5" 662 | object-inspect "^1.13.3" 663 | side-channel-map "^1.0.1" 664 | 665 | side-channel@^1.1.0: 666 | version "1.1.0" 667 | resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" 668 | integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== 669 | dependencies: 670 | es-errors "^1.3.0" 671 | object-inspect "^1.13.3" 672 | side-channel-list "^1.0.0" 673 | side-channel-map "^1.0.1" 674 | side-channel-weakmap "^1.0.2" 675 | 676 | statuses@2.0.1, statuses@^2.0.1: 677 | version "2.0.1" 678 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" 679 | integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== 680 | 681 | toidentifier@1.0.1: 682 | version "1.0.1" 683 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" 684 | integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== 685 | 686 | tr46@~0.0.3: 687 | version "0.0.3" 688 | resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" 689 | integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== 690 | 691 | type-is@^2.0.0, type-is@^2.0.1: 692 | version "2.0.1" 693 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" 694 | integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== 695 | dependencies: 696 | content-type "^1.0.5" 697 | media-typer "^1.1.0" 698 | mime-types "^3.0.0" 699 | 700 | typescript@^5.8.3: 701 | version "5.8.3" 702 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" 703 | integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== 704 | 705 | undici-types@~5.26.4: 706 | version "5.26.5" 707 | resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" 708 | integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== 709 | 710 | undici-types@~6.21.0: 711 | version "6.21.0" 712 | resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" 713 | integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== 714 | 715 | unpipe@1.0.0: 716 | version "1.0.0" 717 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 718 | integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== 719 | 720 | vary@^1, vary@^1.1.2: 721 | version "1.1.2" 722 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 723 | integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== 724 | 725 | web-streams-polyfill@4.0.0-beta.3: 726 | version "4.0.0-beta.3" 727 | resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" 728 | integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== 729 | 730 | webidl-conversions@^3.0.0: 731 | version "3.0.1" 732 | resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" 733 | integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== 734 | 735 | whatwg-url@^5.0.0: 736 | version "5.0.0" 737 | resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" 738 | integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== 739 | dependencies: 740 | tr46 "~0.0.3" 741 | webidl-conversions "^3.0.0" 742 | 743 | which@^2.0.1: 744 | version "2.0.2" 745 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 746 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 747 | dependencies: 748 | isexe "^2.0.0" 749 | 750 | wrappy@1: 751 | version "1.0.2" 752 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 753 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 754 | 755 | zod-to-json-schema@^3.24.1: 756 | version "3.24.5" 757 | resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz#d1095440b147fb7c2093812a53c54df8d5df50a3" 758 | integrity sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g== 759 | 760 | zod@^3.23.8: 761 | version "3.24.3" 762 | resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.3.tgz#1f40f750a05e477396da64438e0e1c0995dafd87" 763 | integrity sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg== 764 | --------------------------------------------------------------------------------