From 08f3287ae3b9328f9bf9fe69d661391b50fdae95 Mon Sep 17 00:00:00 2001 From: Tyler Straub Date: Mon, 8 Dec 2025 14:14:36 -0800 Subject: [PATCH 01/13] feat: Add multi-instance support and log output cleanup - Replace single activeProcess with Map-based activeProcesses for concurrent instance management - Add instanceId parameter to run_project, get_debug_output, and stop_project tools - Add args parameter to run_project for passing command-line arguments to Godot - Add list_processes tool to view all running instances with status - Add isRunning flag to GodotProcess interface for status tracking - Redirect all debug/log output to console.error (stderr) to prevent MCP protocol interference - Clean log output: strip carriage returns, filter empty lines, trim whitespace - Improve process cleanup to handle multiple instances - Add nextInstanceId counter for auto-generating instance IDs - Retain process output after exit for debugging purposes This enables MMORPG development workflows where multiple Godot instances (server + multiple clients) can run simultaneously with proper isolation and management. --- src/index.ts | 365 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 300 insertions(+), 65 deletions(-) diff --git a/src/index.ts b/src/index.ts index 2ab5a6bac..374b845aa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,9 +36,14 @@ const __dirname = dirname(__filename); * Interface representing a running Godot process */ interface GodotProcess { + id: string; process: any; output: string[]; errors: string[]; + projectPath: string; + scene?: string; + startedAt: Date; + isRunning: boolean; } /** @@ -63,11 +68,12 @@ interface OperationParams { */ class GodotServer { private server: Server; - private activeProcess: GodotProcess | null = null; + private activeProcesses: Map = new Map(); private godotPath: string | null = null; private operationsScriptPath: string; private validatedPaths: Map = new Map(); private strictPathValidation: boolean = false; + private nextInstanceId: number = 1; /** * Parameter name mappings between snake_case and camelCase @@ -163,7 +169,7 @@ class GodotServer { /** * Log debug messages if debug mode is enabled - * Using stderr instead of stdout to avoid interfering with JSON-RPC communication + * Note: Uses console.error to ensure it goes to stderr, not stdout (which is used for MCP protocol) */ private logDebug(message: string): void { if (DEBUG_MODE) { @@ -383,11 +389,15 @@ class GodotServer { */ private async cleanup() { this.logDebug('Cleaning up resources'); - if (this.activeProcess) { - this.logDebug('Killing active Godot process'); - this.activeProcess.process.kill(); - this.activeProcess = null; + for (const [id, godotProcess] of this.activeProcesses.entries()) { + this.logDebug(`Killing Godot process: ${id}`); + try { + godotProcess.process.kill(); + } catch (error) { + this.logDebug(`Error killing process ${id}: ${error}`); + } } + this.activeProcesses.clear(); await this.server.close(); } @@ -685,6 +695,17 @@ class GodotServer { type: 'string', description: 'Optional: Specific scene to run', }, + instanceId: { + type: 'string', + description: 'Optional: Unique identifier for this instance (e.g., "server", "client1", "client2"). If not provided, an auto-generated ID will be used.', + }, + args: { + type: 'array', + items: { + type: 'string', + }, + description: 'Optional: Additional command-line arguments to pass to Godot (e.g., ["--server"] or ["--", "profile=Strauberry"])', + }, }, required: ['projectPath'], }, @@ -694,16 +715,26 @@ class GodotServer { description: 'Get the current debug output and errors', inputSchema: { type: 'object', - properties: {}, + properties: { + instanceId: { + type: 'string', + description: 'Optional: Instance ID to get output for. If not provided, returns output for all instances.', + }, + }, required: [], }, }, { name: 'stop_project', - description: 'Stop the currently running Godot project', + description: 'Stop a running Godot project instance', inputSchema: { type: 'object', - properties: {}, + properties: { + instanceId: { + type: 'string', + description: 'Optional: Instance ID to stop. If not provided, stops all running instances.', + }, + }, required: [], }, }, @@ -913,6 +944,15 @@ class GodotServer { required: ['projectPath'], }, }, + { + name: 'list_processes', + description: 'List all currently running Godot project instances', + inputSchema: { + type: 'object', + properties: {}, + required: [], + }, + }, ], })); @@ -925,9 +965,9 @@ class GodotServer { case 'run_project': return await this.handleRunProject(request.params.arguments); case 'get_debug_output': - return await this.handleGetDebugOutput(); + return await this.handleGetDebugOutput(request.params.arguments); case 'stop_project': - return await this.handleStopProject(); + return await this.handleStopProject(request.params.arguments); case 'get_godot_version': return await this.handleGetGodotVersion(); case 'list_projects': @@ -948,6 +988,8 @@ class GodotServer { return await this.handleGetUid(request.params.arguments); case 'update_project_uids': return await this.handleUpdateProjectUids(request.params.arguments); + case 'list_processes': + return await this.handleListProcesses(); default: throw new McpError( ErrorCode.MethodNotFound, @@ -1071,10 +1113,22 @@ class GodotServer { ); } - // Kill any existing process - if (this.activeProcess) { - this.logDebug('Killing existing Godot process before starting a new one'); - this.activeProcess.process.kill(); + // Generate or use provided instance ID + let instanceId = args.instanceId; + if (!instanceId) { + instanceId = `instance_${this.nextInstanceId++}`; + } + + // Check if instance ID already exists + if (this.activeProcesses.has(instanceId)) { + return this.createErrorResponse( + `Instance ID "${instanceId}" is already in use`, + [ + 'Use a different instanceId', + 'Stop the existing instance first using stop_project', + 'Use list_processes to see all running instances', + ] + ); } const cmdArgs = ['-d', '--path', args.projectPath]; @@ -1082,49 +1136,79 @@ class GodotServer { this.logDebug(`Adding scene parameter: ${args.scene}`); cmdArgs.push(args.scene); } + // Add additional command-line arguments if provided + if (args.args && Array.isArray(args.args)) { + for (const arg of args.args) { + if (typeof arg === 'string' && arg.trim() !== '') { + this.logDebug(`Adding command-line argument: ${arg}`); + cmdArgs.push(arg); + } + } + } - this.logDebug(`Running Godot project: ${args.projectPath}`); - const process = spawn(this.godotPath!, cmdArgs, { stdio: 'pipe' }); + this.logDebug(`Running Godot project: ${args.projectPath} with instanceId: ${instanceId}`); + const godotProcess = spawn(this.godotPath!, cmdArgs, { stdio: 'pipe' }); const output: string[] = []; const errors: string[] = []; - process.stdout?.on('data', (data: Buffer) => { - const lines = data.toString().split('\n'); - output.push(...lines); - lines.forEach((line: string) => { - if (line.trim()) this.logDebug(`[Godot stdout] ${line}`); - }); + godotProcess.stdout?.on('data', (data: Buffer) => { + const rawLines = data.toString().split('\n'); + for (const line of rawLines) { + // Strip carriage returns and trim whitespace + const cleaned = line.replace(/\r/g, '').trimEnd(); + // Only add non-empty lines to output + if (cleaned.length > 0) { + output.push(cleaned); + this.logDebug(`[Godot ${instanceId} stdout] ${cleaned}`); + } + } }); - process.stderr?.on('data', (data: Buffer) => { - const lines = data.toString().split('\n'); - errors.push(...lines); - lines.forEach((line: string) => { - if (line.trim()) this.logDebug(`[Godot stderr] ${line}`); - }); + godotProcess.stderr?.on('data', (data: Buffer) => { + const rawLines = data.toString().split('\n'); + for (const line of rawLines) { + // Strip carriage returns and trim whitespace + const cleaned = line.replace(/\r/g, '').trimEnd(); + // Only add non-empty lines to errors + if (cleaned.length > 0) { + errors.push(cleaned); + this.logDebug(`[Godot ${instanceId} stderr] ${cleaned}`); + } + } }); - process.on('exit', (code: number | null) => { - this.logDebug(`Godot process exited with code ${code}`); - if (this.activeProcess && this.activeProcess.process === process) { - this.activeProcess = null; - } + const processInfo: GodotProcess = { + id: instanceId, + process: godotProcess, + output, + errors, + projectPath: args.projectPath, + scene: args.scene, + startedAt: new Date(), + isRunning: true, + }; + + godotProcess.on('exit', (code: number | null) => { + this.logDebug(`Godot process ${instanceId} exited with code ${code}`); + processInfo.isRunning = false; + // Keep the process info after exit so we can retrieve output + // Only delete if explicitly stopped, not on natural exit + // This allows get_debug_output to still work for recently exited processes }); - process.on('error', (err: Error) => { - console.error('Failed to start Godot process:', err); - if (this.activeProcess && this.activeProcess.process === process) { - this.activeProcess = null; - } + godotProcess.on('error', (err: Error) => { + console.error(`Failed to start Godot process ${instanceId}:`, err); + errors.push(`Process error: ${err.message}`); + // Keep process info even on error so we can see what happened }); - this.activeProcess = { process, output, errors }; + this.activeProcesses.set(instanceId, processInfo); return { content: [ { type: 'text', - text: `Godot project started in debug mode. Use get_debug_output to see output.`, + text: `Godot project started in debug mode with instanceId: ${instanceId}. Use get_debug_output to see output.`, }, ], }; @@ -1144,25 +1228,162 @@ class GodotServer { /** * Handle the get_debug_output tool */ - private async handleGetDebugOutput() { - if (!this.activeProcess) { + private async handleGetDebugOutput(args: any) { + // Normalize parameters to camelCase + args = this.normalizeParameters(args || {}); + + if (this.activeProcesses.size === 0) { + return this.createErrorResponse( + 'No active Godot processes.', + [ + 'Use run_project to start a Godot project first', + 'Check if the Godot processes crashed unexpectedly', + ] + ); + } + + // If instanceId is provided, return output for that specific instance + if (args.instanceId) { + const process = this.activeProcesses.get(args.instanceId); + if (!process) { + return this.createErrorResponse( + `No process found with instanceId: ${args.instanceId}`, + [ + 'Use list_processes to see all running instances', + 'Check if the instance has already exited', + ] + ); + } + + return { + content: [ + { + type: 'text', + text: JSON.stringify( + { + instanceId: process.id, + projectPath: process.projectPath, + scene: process.scene, + startedAt: process.startedAt.toISOString(), + isRunning: process.isRunning, + output: process.output, + errors: process.errors, + }, + null, + 2 + ), + }, + ], + }; + } + + // Return output for all instances + const allOutputs: any[] = []; + for (const [id, process] of this.activeProcesses.entries()) { + allOutputs.push({ + instanceId: process.id, + projectPath: process.projectPath, + scene: process.scene, + startedAt: process.startedAt.toISOString(), + isRunning: process.isRunning, + output: process.output, + errors: process.errors, + }); + } + + return { + content: [ + { + type: 'text', + text: JSON.stringify(allOutputs, null, 2), + }, + ], + }; + } + + /** + * Handle the stop_project tool + */ + private async handleStopProject(args: any) { + // Normalize parameters to camelCase + args = this.normalizeParameters(args || {}); + + if (this.activeProcesses.size === 0) { return this.createErrorResponse( - 'No active Godot process.', + 'No active Godot processes to stop.', [ 'Use run_project to start a Godot project first', - 'Check if the Godot process crashed unexpectedly', + 'The processes may have already terminated', ] ); } + // If instanceId is provided, stop that specific instance + if (args.instanceId) { + const process = this.activeProcesses.get(args.instanceId); + if (!process) { + return this.createErrorResponse( + `No process found with instanceId: ${args.instanceId}`, + [ + 'Use list_processes to see all running instances', + 'Check if the instance has already exited', + ] + ); + } + + this.logDebug(`Stopping Godot process: ${args.instanceId}`); + process.process.kill(); + const output = process.output; + const errors = process.errors; + this.activeProcesses.delete(args.instanceId); + + return { + content: [ + { + type: 'text', + text: JSON.stringify( + { + message: `Godot project instance "${args.instanceId}" stopped`, + instanceId: args.instanceId, + finalOutput: output, + finalErrors: errors, + }, + null, + 2 + ), + }, + ], + }; + } + + // Stop all instances + this.logDebug('Stopping all Godot processes'); + const stoppedInstances: any[] = []; + + for (const [id, process] of this.activeProcesses.entries()) { + try { + process.process.kill(); + stoppedInstances.push({ + instanceId: process.id, + projectPath: process.projectPath, + finalOutput: process.output, + finalErrors: process.errors, + }); + } catch (error) { + this.logDebug(`Error stopping process ${id}: ${error}`); + } + } + + this.activeProcesses.clear(); + return { content: [ { type: 'text', text: JSON.stringify( { - output: this.activeProcess.output, - errors: this.activeProcess.errors, + message: 'All Godot project instances stopped', + stoppedInstances, }, null, 2 @@ -1173,24 +1394,39 @@ class GodotServer { } /** - * Handle the stop_project tool + * Handle the list_processes tool */ - private async handleStopProject() { - if (!this.activeProcess) { - return this.createErrorResponse( - 'No active Godot process to stop.', - [ - 'Use run_project to start a Godot project first', - 'The process may have already terminated', - ] - ); + private async handleListProcesses() { + if (this.activeProcesses.size === 0) { + return { + content: [ + { + type: 'text', + text: JSON.stringify( + { + message: 'No running Godot processes', + processes: [], + }, + null, + 2 + ), + }, + ], + }; } - this.logDebug('Stopping active Godot process'); - this.activeProcess.process.kill(); - const output = this.activeProcess.output; - const errors = this.activeProcess.errors; - this.activeProcess = null; + const processes: any[] = []; + for (const [id, process] of this.activeProcesses.entries()) { + processes.push({ + instanceId: process.id, + projectPath: process.projectPath, + scene: process.scene, + startedAt: process.startedAt.toISOString(), + isRunning: !process.process.killed, + outputLines: process.output.length, + errorLines: process.errors.length, + }); + } return { content: [ @@ -1198,9 +1434,8 @@ class GodotServer { type: 'text', text: JSON.stringify( { - message: 'Godot project stopped', - finalOutput: output, - finalErrors: errors, + count: processes.length, + processes, }, null, 2 From 1f79268e847e453222edccdbf4867dd20cc49051 Mon Sep 17 00:00:00 2001 From: Tyler Straub Date: Mon, 8 Dec 2025 14:22:17 -0800 Subject: [PATCH 02/13] docs: Update README with multi-instance support documentation --- README.md | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fcc253968..6fe0b33f2 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,12 @@ This direct feedback loop helps AI assistants like Claude understand what works - **Launch Godot Editor**: Open the Godot editor for a specific project - **Run Godot Projects**: Execute Godot projects in debug mode -- **Capture Debug Output**: Retrieve console output and error messages -- **Control Execution**: Start and stop Godot projects programmatically + - **Multi-Instance Support**: Run multiple Godot instances simultaneously (e.g., dedicated server + multiple clients) + - **Custom Command-Line Arguments**: Pass additional arguments to Godot (e.g., `--server`, `--headless`, `profile=X`, `port=Y`) + - **Instance Management**: Track and manage multiple running instances with unique IDs +- **Capture Debug Output**: Retrieve console output and error messages for specific instances or all instances +- **Control Execution**: Start and stop Godot projects programmatically (individual instances or all at once) +- **List Running Processes**: View all currently running Godot instances and their status - **Get Godot Version**: Retrieve the installed Godot version - **List Godot Projects**: Find Godot projects in a specified directory - **Project Analysis**: Get detailed information about project structure @@ -120,6 +124,7 @@ Add to your Cline MCP settings file (`~/Library/Application Support/Code/User/gl "run_project", "get_debug_output", "stop_project", + "list_processes", "get_godot_version", "list_projects", "get_project_info", @@ -183,6 +188,16 @@ Once configured, your AI assistant will automatically run the MCP server when ne "Run my Godot project and show me any errors" +"Run my Godot project as a dedicated server on port 8080 in headless mode" + +"Launch a client instance with profile 'Strauberry' connecting to localhost:8080" + +"List all currently running Godot instances" + +"Get debug output for the server instance" + +"Stop the client instance with ID 'client1'" + "Get information about my Godot project structure" "Analyze my Godot project structure and suggest improvements" @@ -223,6 +238,24 @@ This architecture provides several benefits: The bundled script accepts operation type and parameters as JSON, allowing for flexible and dynamic operation execution without generating temporary files for each operation. +### Multi-Instance Support + +The MCP server supports running multiple Godot instances simultaneously, which is essential for MMORPG development and multiplayer testing: + +- **Instance IDs**: Each running instance can be assigned a unique identifier (e.g., "server", "client1", "client2") or use auto-generated IDs +- **Command-Line Arguments**: Pass custom arguments to each instance (e.g., `--server`, `--headless`, `profile=X`, `port=Y`) +- **Instance Management**: + - Use `list_processes` to see all running instances and their status + - Use `get_debug_output` with an `instanceId` to get output for a specific instance + - Use `stop_project` with an `instanceId` to stop a specific instance, or without to stop all instances + +**Example Workflow for MMORPG Development:** +1. Launch dedicated server: `run_project` with `instanceId: "server"` and `args: ["--", "--server", "port=8080", "--headless"]` +2. Launch client instances: `run_project` with `instanceId: "client1"` and `args: ["--", "profile=Strauberry", "ip=127.0.0.1", "port=8080"]` +3. Monitor instances: Use `list_processes` to see all running instances +4. Get specific output: Use `get_debug_output` with `instanceId: "server"` to see server logs +5. Stop instances individually: Use `stop_project` with `instanceId: "client1"` to stop a specific client + ## Troubleshooting - **Godot Not Found**: Set the GODOT_PATH environment variable to your Godot executable From 93a12eae9dd99f7b0e6d5e5b76902f945c891ed5 Mon Sep 17 00:00:00 2001 From: Tyler Straub Date: Mon, 8 Dec 2025 14:37:44 -0800 Subject: [PATCH 03/13] docs: remove personal information and make examples generic --- README.md | 8 ++++---- src/index.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6fe0b33f2..824bd0c20 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ Once configured, your AI assistant will automatically run the MCP server when ne "Run my Godot project as a dedicated server on port 8080 in headless mode" -"Launch a client instance with profile 'Strauberry' connecting to localhost:8080" +"Launch a client instance with custom profile connecting to localhost:8080" "List all currently running Godot instances" @@ -240,7 +240,7 @@ The bundled script accepts operation type and parameters as JSON, allowing for f ### Multi-Instance Support -The MCP server supports running multiple Godot instances simultaneously, which is essential for MMORPG development and multiplayer testing: +The MCP server supports running multiple Godot instances simultaneously, which is essential for multiplayer game development and testing: - **Instance IDs**: Each running instance can be assigned a unique identifier (e.g., "server", "client1", "client2") or use auto-generated IDs - **Command-Line Arguments**: Pass custom arguments to each instance (e.g., `--server`, `--headless`, `profile=X`, `port=Y`) @@ -249,9 +249,9 @@ The MCP server supports running multiple Godot instances simultaneously, which i - Use `get_debug_output` with an `instanceId` to get output for a specific instance - Use `stop_project` with an `instanceId` to stop a specific instance, or without to stop all instances -**Example Workflow for MMORPG Development:** +**Example Workflow for Multiplayer Development:** 1. Launch dedicated server: `run_project` with `instanceId: "server"` and `args: ["--", "--server", "port=8080", "--headless"]` -2. Launch client instances: `run_project` with `instanceId: "client1"` and `args: ["--", "profile=Strauberry", "ip=127.0.0.1", "port=8080"]` +2. Launch client instances: `run_project` with `instanceId: "client1"` and `args: ["--", "profile=player1", "ip=127.0.0.1", "port=8080"]` 3. Monitor instances: Use `list_processes` to see all running instances 4. Get specific output: Use `get_debug_output` with `instanceId: "server"` to see server logs 5. Stop instances individually: Use `stop_project` with `instanceId: "client1"` to stop a specific client diff --git a/src/index.ts b/src/index.ts index 374b845aa..a2f03b3b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -704,7 +704,7 @@ class GodotServer { items: { type: 'string', }, - description: 'Optional: Additional command-line arguments to pass to Godot (e.g., ["--server"] or ["--", "profile=Strauberry"])', + description: 'Optional: Additional command-line arguments to pass to Godot (e.g., ["--server"] or ["--", "profile=player1"])', }, }, required: ['projectPath'], From eeec4922b826aa58e408d1fed632a0cf04485d63 Mon Sep 17 00:00:00 2001 From: Tyler Straub Date: Mon, 8 Dec 2025 19:45:12 -0800 Subject: [PATCH 04/13] Fix: Update process status on error events for consistency --- README.md | 58 ++++++- src/index.ts | 456 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 476 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 824bd0c20..83fe75f58 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,15 @@ This direct feedback loop helps AI assistants like Claude understand what works - **Launch Godot Editor**: Open the Godot editor for a specific project - **Run Godot Projects**: Execute Godot projects in debug mode - **Multi-Instance Support**: Run multiple Godot instances simultaneously (e.g., dedicated server + multiple clients) + - **Batch Launch**: Launch multiple instances in a single call with `run_multiple_projects` + - **Staggered Startup**: Use `delayMs` to stagger instance launches (e.g., start server before clients) - **Custom Command-Line Arguments**: Pass additional arguments to Godot (e.g., `--server`, `--headless`, `profile=X`, `port=Y`) - **Instance Management**: Track and manage multiple running instances with unique IDs + - **Bounded Output Buffers**: Automatic memory management for long-running instances (keeps last 1000 lines) + - **Auto-Cleanup**: Stale exited processes are automatically cleaned up after 10 minutes - **Capture Debug Output**: Retrieve console output and error messages for specific instances or all instances -- **Control Execution**: Start and stop Godot projects programmatically (individual instances or all at once) +- **Control Execution**: Start and stop Godot projects programmatically + - Stop individual instances, multiple specific instances, or all at once - **List Running Processes**: View all currently running Godot instances and their status - **Get Godot Version**: Retrieve the installed Godot version - **List Godot Projects**: Find Godot projects in a specified directory @@ -125,6 +130,7 @@ Add to your Cline MCP settings file (`~/Library/Application Support/Code/User/gl "get_debug_output", "stop_project", "list_processes", + "run_multiple_projects", "get_godot_version", "list_projects", "get_project_info", @@ -192,8 +198,12 @@ Once configured, your AI assistant will automatically run the MCP server when ne "Launch a client instance with custom profile connecting to localhost:8080" +"Launch a server and two clients for my multiplayer game, with the server starting first" + "List all currently running Godot instances" +"Stop all client instances but keep the server running" + "Get debug output for the server instance" "Stop the client instance with ID 'client1'" @@ -244,17 +254,55 @@ The MCP server supports running multiple Godot instances simultaneously, which i - **Instance IDs**: Each running instance can be assigned a unique identifier (e.g., "server", "client1", "client2") or use auto-generated IDs - **Command-Line Arguments**: Pass custom arguments to each instance (e.g., `--server`, `--headless`, `profile=X`, `port=Y`) +- **Batch Launch**: Use `run_multiple_projects` to launch multiple instances in a single tool call +- **Staggered Startup**: Use `delayMs` parameter to delay instance launches (e.g., start server 2 seconds before clients) - **Instance Management**: - Use `list_processes` to see all running instances and their status - Use `get_debug_output` with an `instanceId` to get output for a specific instance - - Use `stop_project` with an `instanceId` to stop a specific instance, or without to stop all instances + - Use `stop_project` with `instanceId` (single) or `instanceIds` (array) to stop specific instances, or without parameters to stop all **Example Workflow for Multiplayer Development:** + +**Option 1: Single Tool Call (Recommended)** + +Use `run_multiple_projects` to launch everything at once with staggered delays: + +```json +{ + "instances": [ + { + "projectPath": "/path/to/project", + "instanceId": "server", + "args": ["--", "--server", "port=8080", "--headless"], + "delayMs": 0 + }, + { + "projectPath": "/path/to/project", + "instanceId": "client1", + "args": ["--", "profile=player1", "ip=127.0.0.1", "port=8080"], + "delayMs": 2000 + }, + { + "projectPath": "/path/to/project", + "instanceId": "client2", + "args": ["--", "profile=player2", "ip=127.0.0.1", "port=8080"], + "delayMs": 2000 + } + ] +} +``` + +**Option 2: Individual Tool Calls** + 1. Launch dedicated server: `run_project` with `instanceId: "server"` and `args: ["--", "--server", "port=8080", "--headless"]` 2. Launch client instances: `run_project` with `instanceId: "client1"` and `args: ["--", "profile=player1", "ip=127.0.0.1", "port=8080"]` -3. Monitor instances: Use `list_processes` to see all running instances -4. Get specific output: Use `get_debug_output` with `instanceId: "server"` to see server logs -5. Stop instances individually: Use `stop_project` with `instanceId: "client1"` to stop a specific client + +**Managing Instances:** + +- Monitor instances: Use `list_processes` to see all running instances +- Get specific output: Use `get_debug_output` with `instanceId: "server"` to see server logs +- Stop specific instances: Use `stop_project` with `instanceIds: ["client1", "client2"]` to stop multiple clients +- Stop all instances: Use `stop_project` without parameters ## Troubleshooting diff --git a/src/index.ts b/src/index.ts index a2f03b3b5..245d601c2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,10 +42,29 @@ interface GodotProcess { errors: string[]; projectPath: string; scene?: string; + args?: string[]; startedAt: Date; isRunning: boolean; + exitedAt?: Date; } +/** + * Interface for instance configuration in batch operations + */ +interface InstanceConfig { + projectPath: string; + instanceId?: string; + scene?: string; + args?: string[]; + delayMs?: number; +} + +/** + * Configuration for output buffer limits + */ +const OUTPUT_BUFFER_LIMIT = 1000; // Max lines to keep per stream +const STALE_PROCESS_CLEANUP_MS = 10 * 60 * 1000; // 10 minutes + /** * Interface for server configuration */ @@ -74,6 +93,7 @@ class GodotServer { private validatedPaths: Map = new Map(); private strictPathValidation: boolean = false; private nextInstanceId: number = 1; + private cleanupInterval: NodeJS.Timeout | null = null; /** * Parameter name mappings between snake_case and camelCase @@ -160,6 +180,9 @@ class GodotServer { // Error handling this.server.onerror = (error) => console.error('[MCP Error]', error); + // Start periodic cleanup of stale exited processes + this.startStaleProcessCleanup(); + // Cleanup on exit process.on('SIGINT', async () => { await this.cleanup(); @@ -167,6 +190,49 @@ class GodotServer { }); } + /** + * Start periodic cleanup of stale exited processes + */ + private startStaleProcessCleanup(): void { + // Run cleanup every minute + this.cleanupInterval = setInterval(() => { + this.cleanupStaleProcesses(); + }, 60 * 1000); + } + + /** + * Clean up processes that have exited and been stale for too long + */ + private cleanupStaleProcesses(): void { + const now = Date.now(); + const toDelete: string[] = []; + + for (const [id, process] of this.activeProcesses.entries()) { + if (!process.isRunning && process.exitedAt) { + const staleDuration = now - process.exitedAt.getTime(); + if (staleDuration > STALE_PROCESS_CLEANUP_MS) { + this.logDebug(`Cleaning up stale process: ${id} (exited ${Math.round(staleDuration / 1000)}s ago)`); + toDelete.push(id); + } + } + } + + for (const id of toDelete) { + this.activeProcesses.delete(id); + } + } + + /** + * Add a line to a bounded buffer, removing oldest entries if limit exceeded + */ + private addToBoundedBuffer(buffer: string[], line: string): void { + buffer.push(line); + // Remove oldest entries if we exceed the limit + while (buffer.length > OUTPUT_BUFFER_LIMIT) { + buffer.shift(); + } + } + /** * Log debug messages if debug mode is enabled * Note: Uses console.error to ensure it goes to stderr, not stdout (which is used for MCP protocol) @@ -389,6 +455,13 @@ class GodotServer { */ private async cleanup() { this.logDebug('Cleaning up resources'); + + // Clear the cleanup interval + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval); + this.cleanupInterval = null; + } + for (const [id, godotProcess] of this.activeProcesses.entries()) { this.logDebug(`Killing Godot process: ${id}`); try { @@ -726,13 +799,18 @@ class GodotServer { }, { name: 'stop_project', - description: 'Stop a running Godot project instance', + description: 'Stop running Godot project instance(s)', inputSchema: { type: 'object', properties: { instanceId: { type: 'string', - description: 'Optional: Instance ID to stop. If not provided, stops all running instances.', + description: 'Optional: Single instance ID to stop.', + }, + instanceIds: { + type: 'array', + items: { type: 'string' }, + description: 'Optional: Array of instance IDs to stop. Use this to stop multiple specific instances in one call.', }, }, required: [], @@ -953,6 +1031,47 @@ class GodotServer { required: [], }, }, + { + name: 'run_multiple_projects', + description: 'Launch multiple Godot project instances in a single call. Useful for multiplayer testing (server + clients).', + inputSchema: { + type: 'object', + properties: { + instances: { + type: 'array', + items: { + type: 'object', + properties: { + projectPath: { + type: 'string', + description: 'Path to the Godot project directory', + }, + instanceId: { + type: 'string', + description: 'Optional: Unique identifier for this instance (e.g., "server", "client1")', + }, + scene: { + type: 'string', + description: 'Optional: Specific scene to run', + }, + args: { + type: 'array', + items: { type: 'string' }, + description: 'Optional: Command-line arguments for this instance', + }, + delayMs: { + type: 'number', + description: 'Optional: Delay in milliseconds before launching this instance (useful for staggering server/client startup)', + }, + }, + required: ['projectPath'], + }, + description: 'Array of instance configurations to launch', + }, + }, + required: ['instances'], + }, + }, ], })); @@ -990,6 +1109,8 @@ class GodotServer { return await this.handleUpdateProjectUids(request.params.arguments); case 'list_processes': return await this.handleListProcesses(); + case 'run_multiple_projects': + return await this.handleRunMultipleProjects(request.params.arguments); default: throw new McpError( ErrorCode.MethodNotFound, @@ -1151,6 +1272,18 @@ class GodotServer { const output: string[] = []; const errors: string[] = []; + const processInfo: GodotProcess = { + id: instanceId, + process: godotProcess, + output, + errors, + projectPath: args.projectPath, + scene: args.scene, + args: args.args, + startedAt: new Date(), + isRunning: true, + }; + godotProcess.stdout?.on('data', (data: Buffer) => { const rawLines = data.toString().split('\n'); for (const line of rawLines) { @@ -1158,7 +1291,7 @@ class GodotServer { const cleaned = line.replace(/\r/g, '').trimEnd(); // Only add non-empty lines to output if (cleaned.length > 0) { - output.push(cleaned); + this.addToBoundedBuffer(processInfo.output, cleaned); this.logDebug(`[Godot ${instanceId} stdout] ${cleaned}`); } } @@ -1171,35 +1304,25 @@ class GodotServer { const cleaned = line.replace(/\r/g, '').trimEnd(); // Only add non-empty lines to errors if (cleaned.length > 0) { - errors.push(cleaned); + this.addToBoundedBuffer(processInfo.errors, cleaned); this.logDebug(`[Godot ${instanceId} stderr] ${cleaned}`); } } }); - const processInfo: GodotProcess = { - id: instanceId, - process: godotProcess, - output, - errors, - projectPath: args.projectPath, - scene: args.scene, - startedAt: new Date(), - isRunning: true, - }; - godotProcess.on('exit', (code: number | null) => { this.logDebug(`Godot process ${instanceId} exited with code ${code}`); processInfo.isRunning = false; + processInfo.exitedAt = new Date(); // Keep the process info after exit so we can retrieve output - // Only delete if explicitly stopped, not on natural exit - // This allows get_debug_output to still work for recently exited processes + // Stale processes will be cleaned up by the periodic cleanup task }); godotProcess.on('error', (err: Error) => { console.error(`Failed to start Godot process ${instanceId}:`, err); - errors.push(`Process error: ${err.message}`); - // Keep process info even on error so we can see what happened + processInfo.errors.push(`Process error: ${err.message}`); + processInfo.isRunning = false; + processInfo.exitedAt = new Date(); }); this.activeProcesses.set(instanceId, processInfo); @@ -1318,12 +1441,69 @@ class GodotServer { ); } - // If instanceId is provided, stop that specific instance - if (args.instanceId) { - const process = this.activeProcesses.get(args.instanceId); + // Helper function to stop a single instance + const stopInstance = (instanceId: string): { success: boolean; result?: any; error?: string } => { + const process = this.activeProcesses.get(instanceId); if (!process) { + return { success: false, error: `No process found with instanceId: ${instanceId}` }; + } + + this.logDebug(`Stopping Godot process: ${instanceId}`); + try { + process.process.kill(); + const result = { + instanceId: process.id, + projectPath: process.projectPath, + finalOutput: process.output, + finalErrors: process.errors, + }; + this.activeProcesses.delete(instanceId); + return { success: true, result }; + } catch (error) { + this.logDebug(`Error stopping process ${instanceId}: ${error}`); + return { success: false, error: `Failed to stop ${instanceId}: ${error}` }; + } + }; + + // If instanceIds array is provided, stop those specific instances + if (args.instanceIds && Array.isArray(args.instanceIds) && args.instanceIds.length > 0) { + this.logDebug(`Stopping multiple Godot processes: ${args.instanceIds.join(', ')}`); + const stoppedInstances: any[] = []; + const errors: string[] = []; + + for (const instanceId of args.instanceIds) { + const result = stopInstance(instanceId); + if (result.success && result.result) { + stoppedInstances.push(result.result); + } else if (result.error) { + errors.push(result.error); + } + } + + return { + content: [ + { + type: 'text', + text: JSON.stringify( + { + message: `Stopped ${stoppedInstances.length} of ${args.instanceIds.length} requested instances`, + stoppedInstances, + errors: errors.length > 0 ? errors : undefined, + }, + null, + 2 + ), + }, + ], + }; + } + + // If single instanceId is provided, stop that specific instance + if (args.instanceId) { + const result = stopInstance(args.instanceId); + if (!result.success) { return this.createErrorResponse( - `No process found with instanceId: ${args.instanceId}`, + result.error || `Failed to stop instance: ${args.instanceId}`, [ 'Use list_processes to see all running instances', 'Check if the instance has already exited', @@ -1331,12 +1511,6 @@ class GodotServer { ); } - this.logDebug(`Stopping Godot process: ${args.instanceId}`); - process.process.kill(); - const output = process.output; - const errors = process.errors; - this.activeProcesses.delete(args.instanceId); - return { content: [ { @@ -1344,9 +1518,7 @@ class GodotServer { text: JSON.stringify( { message: `Godot project instance "${args.instanceId}" stopped`, - instanceId: args.instanceId, - finalOutput: output, - finalErrors: errors, + ...result.result, }, null, 2 @@ -1421,8 +1593,10 @@ class GodotServer { instanceId: process.id, projectPath: process.projectPath, scene: process.scene, + args: process.args, startedAt: process.startedAt.toISOString(), - isRunning: !process.process.killed, + exitedAt: process.exitedAt?.toISOString(), + isRunning: process.isRunning, outputLines: process.output.length, errorLines: process.errors.length, }); @@ -1445,6 +1619,222 @@ class GodotServer { }; } + /** + * Handle the run_multiple_projects tool + * Launches multiple Godot instances with optional staggered delays + */ + private async handleRunMultipleProjects(args: any) { + // Normalize parameters to camelCase + args = this.normalizeParameters(args || {}); + + if (!args.instances || !Array.isArray(args.instances) || args.instances.length === 0) { + return this.createErrorResponse( + 'instances array is required and must not be empty', + ['Provide an array of instance configurations with at least one entry'] + ); + } + + // Ensure godotPath is set + if (!this.godotPath) { + await this.detectGodotPath(); + if (!this.godotPath) { + return this.createErrorResponse( + 'Could not find a valid Godot executable path', + [ + 'Ensure Godot is installed correctly', + 'Set GODOT_PATH environment variable to specify the correct path', + ] + ); + } + } + + // Validate all instances first before launching any + const validatedInstances: InstanceConfig[] = []; + const validationErrors: string[] = []; + + for (let i = 0; i < args.instances.length; i++) { + const instance = this.normalizeParameters(args.instances[i]) as InstanceConfig; + + if (!instance.projectPath) { + validationErrors.push(`Instance ${i}: projectPath is required`); + continue; + } + + if (!this.validatePath(instance.projectPath)) { + validationErrors.push(`Instance ${i}: Invalid project path`); + continue; + } + + const projectFile = join(instance.projectPath, 'project.godot'); + if (!existsSync(projectFile)) { + validationErrors.push(`Instance ${i}: Not a valid Godot project: ${instance.projectPath}`); + continue; + } + + // Generate instanceId if not provided + if (!instance.instanceId) { + instance.instanceId = `instance_${this.nextInstanceId++}`; + } + + // Check for duplicate instanceId + if (this.activeProcesses.has(instance.instanceId)) { + validationErrors.push(`Instance ${i}: instanceId "${instance.instanceId}" is already in use`); + continue; + } + + // Check for duplicates within this batch + if (validatedInstances.some(v => v.instanceId === instance.instanceId)) { + validationErrors.push(`Instance ${i}: duplicate instanceId "${instance.instanceId}" in batch`); + continue; + } + + validatedInstances.push(instance); + } + + if (validationErrors.length > 0) { + return this.createErrorResponse( + `Validation failed for ${validationErrors.length} instance(s):\n${validationErrors.join('\n')}`, + [ + 'Fix the validation errors and try again', + 'Use list_processes to see currently running instances', + ] + ); + } + + // Sort instances by delayMs (ascending) for proper staggering + validatedInstances.sort((a, b) => (a.delayMs || 0) - (b.delayMs || 0)); + + // Launch instances with delays + const launchedInstances: any[] = []; + const launchErrors: string[] = []; + + const launchInstance = (instance: InstanceConfig): { success: boolean; result?: any; error?: string } => { + try { + const cmdArgs = ['-d', '--path', instance.projectPath]; + + if (instance.scene && this.validatePath(instance.scene)) { + cmdArgs.push(instance.scene); + } + + if (instance.args && Array.isArray(instance.args)) { + for (const arg of instance.args) { + if (typeof arg === 'string' && arg.trim() !== '') { + cmdArgs.push(arg); + } + } + } + + this.logDebug(`Launching Godot instance: ${instance.instanceId} with args: ${cmdArgs.join(' ')}`); + const godotProcess = spawn(this.godotPath!, cmdArgs, { stdio: 'pipe' }); + const output: string[] = []; + const errors: string[] = []; + + const processInfo: GodotProcess = { + id: instance.instanceId!, + process: godotProcess, + output, + errors, + projectPath: instance.projectPath, + scene: instance.scene, + args: instance.args, + startedAt: new Date(), + isRunning: true, + }; + + godotProcess.stdout?.on('data', (data: Buffer) => { + const rawLines = data.toString().split('\n'); + for (const line of rawLines) { + const cleaned = line.replace(/\r/g, '').trimEnd(); + if (cleaned.length > 0) { + this.addToBoundedBuffer(processInfo.output, cleaned); + this.logDebug(`[Godot ${instance.instanceId} stdout] ${cleaned}`); + } + } + }); + + godotProcess.stderr?.on('data', (data: Buffer) => { + const rawLines = data.toString().split('\n'); + for (const line of rawLines) { + const cleaned = line.replace(/\r/g, '').trimEnd(); + if (cleaned.length > 0) { + this.addToBoundedBuffer(processInfo.errors, cleaned); + this.logDebug(`[Godot ${instance.instanceId} stderr] ${cleaned}`); + } + } + }); + + godotProcess.on('exit', (code: number | null) => { + this.logDebug(`Godot process ${instance.instanceId} exited with code ${code}`); + processInfo.isRunning = false; + processInfo.exitedAt = new Date(); + }); + + godotProcess.on('error', (err: Error) => { + console.error(`Failed to start Godot process ${instance.instanceId}:`, err); + processInfo.errors.push(`Process error: ${err.message}`); + processInfo.isRunning = false; + processInfo.exitedAt = new Date(); + }); + + this.activeProcesses.set(instance.instanceId!, processInfo); + + return { + success: true, + result: { + instanceId: instance.instanceId, + projectPath: instance.projectPath, + scene: instance.scene, + args: instance.args, + delayMs: instance.delayMs || 0, + }, + }; + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + return { success: false, error: `Failed to launch ${instance.instanceId}: ${errorMessage}` }; + } + }; + + // Launch with delays using async/await + const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + let lastDelay = 0; + + for (const instance of validatedInstances) { + const targetDelay = instance.delayMs || 0; + const waitTime = targetDelay - lastDelay; + + if (waitTime > 0) { + this.logDebug(`Waiting ${waitTime}ms before launching ${instance.instanceId}`); + await sleep(waitTime); + } + + lastDelay = targetDelay; + + const result = launchInstance(instance); + if (result.success && result.result) { + launchedInstances.push(result.result); + } else if (result.error) { + launchErrors.push(result.error); + } + } + + return { + content: [ + { + type: 'text', + text: JSON.stringify( + { + message: `Launched ${launchedInstances.length} of ${validatedInstances.length} instances`, + launchedInstances, + errors: launchErrors.length > 0 ? launchErrors : undefined, + }, + null, + 2 + ), + }, + ], + }; + } + /** * Handle the get_godot_version tool */ From b6b2fd53b41aacf5dfe67ca960ddac9077e7fdbd Mon Sep 17 00:00:00 2001 From: "qqhyyy.com" <60999@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:42:43 +0800 Subject: [PATCH 05/13] refactor: Enhance instance ID management and process cleanup - Update the description for the 'list_processes' tool to clarify it tracks both running and recently exited instances. - Improve instance ID validation to allow reuse of IDs for exited processes. - Ensure only running processes are killed during stop operations, while still tracking all instances. - Add logging for cleaning up exited processes to aid in debugging. --- src/index.ts | 62 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/src/index.ts b/src/index.ts index 245d601c2..b55285b8e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1024,7 +1024,7 @@ class GodotServer { }, { name: 'list_processes', - description: 'List all currently running Godot project instances', + description: 'List all tracked Godot project instances (both running and recently exited)', inputSchema: { type: 'object', properties: {}, @@ -1240,16 +1240,23 @@ class GodotServer { instanceId = `instance_${this.nextInstanceId++}`; } - // Check if instance ID already exists - if (this.activeProcesses.has(instanceId)) { - return this.createErrorResponse( - `Instance ID "${instanceId}" is already in use`, - [ - 'Use a different instanceId', - 'Stop the existing instance first using stop_project', - 'Use list_processes to see all running instances', - ] - ); + // Check if instance ID already exists and is running + const existingProcess = this.activeProcesses.get(instanceId); + if (existingProcess) { + if (existingProcess.isRunning) { + return this.createErrorResponse( + `Instance ID "${instanceId}" is already in use by a running process`, + [ + 'Use a different instanceId', + 'Stop the existing instance first using stop_project', + 'Use list_processes to see all running instances', + ] + ); + } else { + // Clean up exited process to allow reuse of the ID + this.logDebug(`Cleaning up exited process ${instanceId} to allow ID reuse`); + this.activeProcesses.delete(instanceId); + } } const cmdArgs = ['-d', '--path', args.projectPath]; @@ -1372,8 +1379,8 @@ class GodotServer { return this.createErrorResponse( `No process found with instanceId: ${args.instanceId}`, [ - 'Use list_processes to see all running instances', - 'Check if the instance has already exited', + 'Use list_processes to see all tracked instances', + 'The instance may have been cleaned up after being exited for too long', ] ); } @@ -1450,10 +1457,14 @@ class GodotServer { this.logDebug(`Stopping Godot process: ${instanceId}`); try { - process.process.kill(); + // Only attempt to kill if the process is still running + if (process.isRunning) { + process.process.kill(); + } const result = { instanceId: process.id, projectPath: process.projectPath, + wasRunning: process.isRunning, finalOutput: process.output, finalErrors: process.errors, }; @@ -1528,16 +1539,20 @@ class GodotServer { }; } - // Stop all instances + // Stop all instances (only kill running ones, but remove all from tracking) this.logDebug('Stopping all Godot processes'); const stoppedInstances: any[] = []; for (const [id, process] of this.activeProcesses.entries()) { try { - process.process.kill(); + // Only attempt to kill if the process is still running + if (process.isRunning) { + process.process.kill(); + } stoppedInstances.push({ instanceId: process.id, projectPath: process.projectPath, + wasRunning: process.isRunning, finalOutput: process.output, finalErrors: process.errors, }); @@ -1676,10 +1691,17 @@ class GodotServer { instance.instanceId = `instance_${this.nextInstanceId++}`; } - // Check for duplicate instanceId - if (this.activeProcesses.has(instance.instanceId)) { - validationErrors.push(`Instance ${i}: instanceId "${instance.instanceId}" is already in use`); - continue; + // Check for duplicate instanceId - only block if process is still running + const existingProcess = this.activeProcesses.get(instance.instanceId); + if (existingProcess) { + if (existingProcess.isRunning) { + validationErrors.push(`Instance ${i}: instanceId "${instance.instanceId}" is already in use by a running process`); + continue; + } else { + // Clean up exited process to allow reuse of the ID + this.logDebug(`Cleaning up exited process ${instance.instanceId} to allow ID reuse`); + this.activeProcesses.delete(instance.instanceId); + } } // Check for duplicates within this batch From a72b70b97b9b9757da86d8c5466507b9b2ed20c4 Mon Sep 17 00:00:00 2001 From: "qqhyyy.com" <60999@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:48:08 +0800 Subject: [PATCH 06/13] docs: Add Chinese documentation and pnpm lock file --- CONTRIBUTING_CN.md | 187 +++++++++++++++++++++++ README_CN.md | 321 ++++++++++++++++++++++++++++++++++++++++ pnpm-lock.yaml | 362 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 870 insertions(+) create mode 100644 CONTRIBUTING_CN.md create mode 100644 README_CN.md create mode 100644 pnpm-lock.yaml diff --git a/CONTRIBUTING_CN.md b/CONTRIBUTING_CN.md new file mode 100644 index 000000000..b356d3a0e --- /dev/null +++ b/CONTRIBUTING_CN.md @@ -0,0 +1,187 @@ +# 为 Godot MCP 做贡献 + +感谢您考虑为 Godot MCP 做贡献!本文档概述了为项目做贡献的流程。 + +## 行为准则 + +通过参与本项目,您同意为每个人保持尊重和包容的环境。 + +## 我可以如何贡献? + +### 报告错误 + +- 检查错误是否已在问题部分报告 +- 如果可用,使用错误报告模板 +- 包含重现错误的详细步骤 +- 包含任何相关的日志或截图 +- 指定您的环境(操作系统、Godot 版本等) + +### 建议增强功能 + +- 检查增强功能是否已在问题部分建议 +- 如果可用,使用功能请求模板 +- 清楚地描述增强功能及其好处 +- 考虑增强功能如何适应项目范围 + +### 拉取请求 + +1. Fork 仓库 +2. 为您的功能或错误修复创建一个新分支(`git checkout -b feature/amazing-feature`) +3. 进行更改 +4. 如果有可用测试,请运行测试 +5. 使用清晰的提交消息提交您的更改 +6. 推送到您的分支(`git push origin feature/amazing-feature`) +7. 打开一个拉取请求 + +## 开发流程 + +### 设置开发环境 + +1. 克隆仓库 +2. 使用 `npm install` 安装依赖项 +3. 使用 `npm run build` 构建项目 +4. 对于自动重新构建的开发,使用 `npm run watch` + +### 项目结构 + +``` +godot-mcp/ +├── src/ # 源代码 +│ └── index.ts # 主服务器实现 +├── build/ # 编译的 JavaScript(生成的) +├── tests/ # 测试文件(未来) +├── examples/ # 示例 Godot 项目(未来) +├── LICENSE # MIT 许可证 +├── README.md # 文档 +├── CONTRIBUTING.md # 贡献指南 +├── package.json # 项目配置 +└── tsconfig.json # TypeScript 配置 +``` + +### 代码风格 + +- 遵循项目中现有的代码风格 +- 使用 TypeScript 以确保类型安全 +- 为所有函数和类包含 JSDoc 注释 +- 编写清晰和描述性的变量和函数名称 +- 为复杂对象使用有意义的接口 +- 使用详细的错误消息优雅地处理错误 + +### 调试 + +要调试 MCP 服务器: + +1. 将 `DEBUG` 环境变量设置为 `true` +2. 使用 MCP Inspector 进行交互式调试: + ```bash + npm run inspector + ``` +3. 检查日志以获取有关正在发生的事情的详细信息 + +### 添加新工具 + +向 MCP 服务器添加新工具时: + +1. 在 `setupToolHandlers` 方法定义工具 +2. 为工具创建处理程序方法 +3. 添加适当的输入验证和错误处理 +4. 使用新工具的文档更新 README.md +5. 更新 README.md 中的功能部分 +6. 更新配置示例中的 autoApprove 部分 +7. 为新功能添加测试 + +#### 最近添加的工具 + +最近添加了以下工具: + +- **get_project_info**:检索有关 Godot 项目的元数据 + - 分析项目结构 + - 返回有关场景、脚本和资源的信息 + - 帮助 LLM 理解 Godot 项目的组织 + +- **capture_screenshot**:截取正在运行的 Godot 项目的屏幕截图 + - 需要活动的 Godot 进程 + - 将屏幕截图保存到指定路径 + - 对于视觉调试和反馈很有用 + +示例: + +```typescript +// 在 setupToolHandlers 中 +{ + name: 'your_new_tool', + description: '描述您的工具做什么', + inputSchema: { + type: 'object', + properties: { + param1: { + type: 'string', + description: '参数 1 的描述', + }, + }, + required: ['param1'], + }, +} + +// 添加处理程序方法 +private async handleYourNewTool(args: any) { + // 验证输入 + if (!args.param1) { + return this.createErrorResponse( + '参数 1 是必需的', + ['为参数 1 提供有效值'] + ); + } + + try { + // 实现工具功能 + // ... + + return { + content: [ + { + type: 'text', + text: '您的工具的结果', + }, + ], + }; + } catch (error: any) { + return this.createErrorResponse( + `执行工具失败:${error?.message || '未知错误'}`, + [ + '可能的解决方案 1', + '可能的解决方案 2' + ] + ); + } +} +``` + +### 跨平台兼容性 + +进行更改时,确保它们在不同平台上工作: + +- 使用 Node.js 的路径工具(`path.join` 等)而不是硬编码的路径分隔符 +- 如果可能,在不同操作系统上测试 +- 考虑不同的 Godot 安装位置 +- 使用环境变量进行配置 + +## 测试 + +- 尽可能为新功能添加测试 +- 在提交拉取请求之前确保所有测试通过 +- 如果可能,在不同平台上测试 +- 使用不同版本的 Godot 进行测试 + +## 文档 + +- 使用新功能保持 README.md 更新 +- 记录所有工具及其参数 +- 为新功能包含示例 +- 使用常见问题更新故障排除部分 + +## 有问题? + +如果您对贡献有任何疑问,请随时打开一个问题进行讨论。 + +感谢您的贡献! diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 000000000..113d2ddfe --- /dev/null +++ b/README_CN.md @@ -0,0 +1,321 @@ +# Godot MCP + +[![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/Coding-Solo) + +[![](https://badge.mcpx.dev?type=server 'MCP Server')](https://modelcontextprotocol.io/introduction) +[![Made with Godot](https://img.shields.io/badge/Made%20with-Godot-478CBF?style=flat&logo=godot%20engine&logoColor=white)](https://godotengine.org) +[![](https://img.shields.io/badge/Node.js-339933?style=flat&logo=nodedotjs&logoColor=white 'Node.js')](https://nodejs.org/en/download/) +[![](https://img.shields.io/badge/TypeScript-3178C6?style=flat&logo=typescript&logoColor=white 'TypeScript')](https://www.typescriptlang.org/) + +[![](https://img.shields.io/github/last-commit/Coding-Solo/godot-mcp 'Last Commit')](https://github.com/Coding-Solo/godot-mcp/commits/main) +[![](https://img.shields.io/github/stars/Coding-Solo/godot-mcp 'Stars')](https://github.com/Coding-Solo/godot-mcp/stargazers) +[![](https://img.shields.io/github/forks/Coding-Solo/godot-mcp 'Forks')](https://github.com/Coding-Solo/godot-mcp/network/members) +[![](https://img.shields.io/badge/License-MIT-red.svg 'MIT License')](https://opensource.org/licenses/MIT) + +```text + ((((((( ((((((( + ((((((((((( ((((((((((( + ((((((((((((( ((((((((((((( + ((((((((((((((((((((((((((((((((( + ((((((((((((((((((((((((((((((((( + ((((( ((((((((((((((((((((((((((((((((((((((((( ((((( + ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + (((((((((((@@@@@@@(((((((((((((((((((((((((((@@@@@@@((((((((((( + (((((((((@@@@,,,,,@@@(((((((((((((((((((((@@@,,,,,@@@@((((((((( + ((((((((@@@,,,,,,,,,@@(((((((@@@@@(((((((@@,,,,,,,,,@@@(((((((( + ((((((((@@@,,,,,,,,,@@(((((((@@@@@(((((((@@,,,,,,,,,@@@(((((((( + (((((((((@@@,,,,,,,@@((((((((@@@@@((((((((@@,,,,,,,@@@((((((((( + ((((((((((((@@@@@@(((((((((((@@@@@(((((((((((@@@@@@(((((((((((( + ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + @@@@@@@@@@@@@((((((((((((@@@@@@@@@@@@@((((((((((((@@@@@@@@@@@@@ + ((((((((( @@@(((((((((((@@(((((((((((@@(((((((((((@@@ ((((((((( + (((((((((( @@((((((((((@@@(((((((((((@@@((((((((((@@ (((((((((( + (((((((((((@@@@@@@@@@@@@@(((((((((((@@@@@@@@@@@@@@((((((((((( + ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + ((((((((((((((((((((((((((((((((((((((((((((((((((((( + ((((((((((((((((((((((((((((((((((((((((((((((( + ((((((((((((((((((((((((((((((((( + + + /$$ /$$ /$$$$$$ /$$$$$$$ + | $$$ /$$$ /$$__ $$| $$__ $$ + | $$$$ /$$$$| $$ \__/| $$ \ $$ + | $$ $$/$$ $$| $$ | $$$$$$$/ + | $$ $$$| $$| $$ | $$____/ + | $$\ $ | $$| $$ $$| $$ + | $$ \/ | $$| $$$$$$/| $$ + |__/ |__/ \______/ |__/ +``` + +一个用于与 Godot 游戏引擎交互的模型上下文协议(MCP)服务器。 + +## 简介 + +Godot MCP 使 AI 助手能够启动 Godot 编辑器、运行项目、捕获调试输出和控制项目执行——所有这些都通过标准化的接口实现。 + +这种直接反馈循环帮助像 Claude 这样的 AI 助手理解在真实的 Godot 项目中什么有效、什么无效,从而提供更好的代码生成和调试协助。 + +## 功能特性 + +- **启动 Godot 编辑器**:为特定项目打开 Godot 编辑器 +- **运行 Godot 项目**:在调试模式下执行 Godot 项目 + - **多实例支持**:同时运行多个 Godot 实例(例如:专用服务器 + 多个客户端) + - **批量启动**:使用 `run_multiple_projects` 在一次调用中启动多个实例 + - **交错启动**:使用 `delayMs` 交错启动实例(例如:先启动服务器再启动客户端) + - **自定义命令行参数**:向 Godot 传递额外参数(例如:`--server`、`--headless`、`profile=X`、`port=Y`) + - **实例管理**:使用唯一 ID 跟踪和管理多个运行中的实例 + - **有界输出缓冲区**:自动内存管理,用于长时间运行的实例(保留最后 1000 行) + - **自动清理**:已退出的进程在 10 分钟后自动清理 +- **捕获调试输出**:检索特定实例或所有实例的控制台输出和错误消息 +- **控制执行**:以编程方式启动和停止 Godot 项目 + - 停止单个实例、多个特定实例或一次性停止所有实例 +- **列出运行中的进程**:查看所有当前运行的 Godot 实例及其状态 +- **获取 Godot 版本**:检索已安装的 Godot 版本 +- **列出 Godot 项目**:在指定目录中查找 Godot 项目 +- **项目分析**:获取项目结构的详细信息 +- **场景管理**: + - 创建具有指定根节点类型的新场景 + - 向现有场景添加节点,并具有可自定义的属性 + - 将精灵和纹理加载到 Sprite2D 节点中 + - 将 3D 场景导出为 GridMap 的 MeshLibrary 资源 + - 保存场景,并提供创建变体的选项 +- **UID 管理**(适用于 Godot 4.4+): + - 获取特定文件的 UID + - 通过重新保存资源来更新 UID 引用 + +## 系统要求 + +- 系统上已安装 [Godot Engine](https://godotengine.org/download) +- Node.js 和 npm +- 支持 MCP 的 AI 助手(Cline、Cursor 等) + +## 安装和配置 + +### 步骤 1:安装和构建 + +首先,克隆仓库并构建 MCP 服务器: + +```bash +git clone https://github.com/Coding-Solo/godot-mcp.git +cd godot-mcp +npm install +npm run build +``` + +### 步骤 2:配置 AI 助手 + +#### 选项 A:配置 Cline + +将以下内容添加到您的 Cline MCP 设置文件(`~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`): + +```json +{ + "mcpServers": { + "godot": { + "command": "node", + "args": ["/absolute/path/to/godot-mcp/build/index.js"], + "env": { + "DEBUG": "true" // 可选:启用详细日志记录 + }, + "disabled": false, + "autoApprove": [ + "launch_editor", + "run_project", + "get_debug_output", + "stop_project", + "list_processes", + "run_multiple_projects", + "get_godot_version", + "list_projects", + "get_project_info", + "create_scene", + "add_node", + "load_sprite", + "export_mesh_library", + "save_scene", + "get_uid", + "update_project_uids" + ] + } + } +} +``` + +#### 选项 B:配置 Cursor + +**使用 Cursor UI:** + +1. 转到 **Cursor 设置** > **功能** > **MCP** +2. 点击 **+ 添加新的 MCP 服务器** 按钮 +3. 填写表单: + - 名称:`godot`(或您喜欢的任何名称) + - 类型:`command` + - 命令:`node /absolute/path/to/godot-mcp/build/index.js` +4. 点击"添加" +5. 您可能需要点击 MCP 服务器卡片右上角的刷新按钮来填充工具列表 + +**使用项目特定配置:** + +在项目目录中创建 `.cursor/mcp.json` 文件,内容如下: + +```json +{ + "mcpServers": { + "godot": { + "command": "node", + "args": ["/absolute/path/to/godot-mcp/build/index.js"], + "env": { + "DEBUG": "true" // 启用详细日志记录 + } + } + } +} +``` + +### 步骤 3:可选的环境变量 + +您可以使用以下环境变量自定义服务器行为: + +- `GODOT_PATH`:Godot 可执行文件的路径(覆盖自动检测) +- `DEBUG`:设置为 "true" 以启用详细的服务器端调试日志 + +## 示例提示词 + +配置完成后,您的 AI 助手将在需要时自动运行 MCP 服务器。您可以使用如下提示词: + +```text +"为 /path/to/project 中的我的项目启动 Godot 编辑器" + +"运行我的 Godot 项目并显示任何错误" + +"以无头模式在端口 8080 上运行我的 Godot 项目作为专用服务器" + +"启动一个连接到 localhost:8080 的自定义配置文件客户端实例" + +"为我的多人游戏启动一个服务器和两个客户端,服务器先启动" + +"列出所有当前运行的 Godot 实例" + +"停止所有客户端实例但保持服务器运行" + +"获取服务器实例的调试输出" + +"停止 ID 为 'client1' 的客户端实例" + +"获取有关我的 Godot 项目结构的信息" + +"分析我的 Godot 项目结构并提出改进建议" + +"帮助我调试 Godot 项目中的这个错误:[粘贴错误]" + +"为具有二段跳和墙壁滑动的角色控制器编写 GDScript" + +"在我的 Godot 项目中创建一个带有 Player 节点的新场景" + +"向我的玩家场景添加 Sprite2D 节点并加载角色纹理" + +"将我的 3D 模型导出为 MeshLibrary 以便与 GridMap 一起使用" + +"创建一个带有按钮和标签的 UI 场景,用于游戏的主菜单" + +"获取我的 Godot 4.4 项目中特定脚本文件的 UID" + +"升级到 4.4 后更新我的 Godot 项目中的 UID 引用" +``` + +## 实现细节 + +### 架构 + +Godot MCP 服务器使用捆绑的 GDScript 方法来处理复杂操作: + +1. **直接命令**:简单的操作(如启动编辑器或获取项目信息)直接使用 Godot 的内置 CLI 命令。 +2. **捆绑操作脚本**:复杂的操作(如创建场景或添加节点)使用单个全面的 GDScript 文件(`godot_operations.gd`)来处理所有操作。 + +这种架构提供了几个好处: + +- **无临时文件**:消除了对临时脚本文件的需求,保持系统整洁 +- **简化的代码库**:将所有 Godot 操作集中在一个(某种程度上)有组织的文件中 +- **更好的可维护性**:更容易添加新操作或修改现有操作 +- **改进的错误处理**:在所有操作中提供一致的错误报告 +- **减少开销**:最小化文件 I/O 操作以提高性能 + +捆绑脚本接受操作类型和参数作为 JSON,允许灵活和动态的操作执行,而无需为每个操作生成临时文件。 + +### 多实例支持 + +MCP 服务器支持同时运行多个 Godot 实例,这对于多人游戏开发和测试至关重要: + +- **实例 ID**:每个运行中的实例都可以分配一个唯一标识符(例如:"server"、"client1"、"client2")或使用自动生成的 ID +- **命令行参数**:向每个实例传递自定义参数(例如:`--server`、`--headless`、`profile=X`、`port=Y`) +- **批量启动**:使用 `run_multiple_projects` 在单个工具调用中启动多个实例 +- **交错启动**:使用 `delayMs` 参数延迟实例启动(例如:在客户端之前 2 秒启动服务器) +- **实例管理**: + - 使用 `list_processes` 查看所有运行中的实例及其状态 + - 使用带有 `instanceId` 的 `get_debug_output` 获取特定实例的输出 + - 使用带有 `instanceId`(单个)或 `instanceIds`(数组)的 `stop_project` 停止特定实例,或不带参数停止所有实例 + +**多人游戏开发示例工作流程:** + +**选项 1:单个工具调用(推荐)** + +使用 `run_multiple_projects` 一次启动所有内容,并带有交错延迟: + +```json +{ + "instances": [ + { + "projectPath": "/path/to/project", + "instanceId": "server", + "args": ["--", "--server", "port=8080", "--headless"], + "delayMs": 0 + }, + { + "projectPath": "/path/to/project", + "instanceId": "client1", + "args": ["--", "profile=player1", "ip=127.0.0.1", "port=8080"], + "delayMs": 2000 + }, + { + "projectPath": "/path/to/project", + "instanceId": "client2", + "args": ["--", "profile=player2", "ip=127.0.0.1", "port=8080"], + "delayMs": 2000 + } + ] +} +``` + +**选项 2:单独的工具调用** + +1. 启动专用服务器:`run_project` 带有 `instanceId: "server"` 和 `args: ["--", "--server", "port=8080", "--headless"]` +2. 启动客户端实例:`run_project` 带有 `instanceId: "client1"` 和 `args: ["--", "profile=player1", "ip=127.0.0.1", "port=8080"]` + +**管理实例:** + +- 监控实例:使用 `list_processes` 查看所有运行中的实例 +- 获取特定输出:使用带有 `instanceId: "server"` 的 `get_debug_output` 查看服务器日志 +- 停止特定实例:使用带有 `instanceIds: ["client1", "client2"]` 的 `stop_project` 停止多个客户端 +- 停止所有实例:使用不带参数的 `stop_project` + +## 故障排除 + +- **未找到 Godot**:将 GODOT_PATH 环境变量设置为您的 Godot 可执行文件 +- **连接问题**:确保服务器正在运行并重新启动您的 AI 助手 +- **无效的项目路径**:确保路径指向包含 project.godot 文件的目录 +- **构建问题**:通过运行 `npm install` 确保已安装所有依赖项 +- **针对 Cursor 特别说明**: + - 确保 MCP 服务器在 Cursor 设置中显示并已启用(设置 > MCP) + - MCP 工具只能使用 Agent 聊天配置文件运行(需要 Cursor Pro 或 Business 订阅) + - 使用"Yolo 模式"自动运行 MCP 工具请求 + +## 许可证 + +本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。 + +[![MseeP.ai Security Assessment Badge](https://mseep.net/pr/coding-solo-godot-mcp-badge.png)](https://mseep.ai/app/coding-solo-godot-mcp) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 000000000..212b128a5 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,362 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@modelcontextprotocol/sdk': + specifier: 0.6.0 + version: 0.6.0 + axios: + specifier: ^1.7.9 + version: 1.13.2 + fs-extra: + specifier: ^11.2.0 + version: 11.3.3 + devDependencies: + '@types/node': + specifier: ^20.11.24 + version: 20.19.27 + typescript: + specifier: ^5.3.3 + version: 5.9.3 + +packages: + + '@modelcontextprotocol/sdk@0.6.0': + resolution: {integrity: sha512-9rsDudGhDtMbvxohPoMMyAUOmEzQsOK+XFchh6gZGqo8sx9sBuZQs+CUttXqa8RZXKDaJRCN2tUtgGof7jRkkw==} + + '@types/node@20.19.27': + resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + fs-extra@11.3.3: + resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} + engines: {node: '>=14.14'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.7.1: + resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} + engines: {node: '>=0.10.0'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@modelcontextprotocol/sdk@0.6.0': + dependencies: + content-type: 1.0.5 + raw-body: 3.0.2 + zod: 3.25.76 + + '@types/node@20.19.27': + dependencies: + undici-types: 6.21.0 + + asynckit@0.4.0: {} + + axios@1.13.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + content-type@1.0.5: {} + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + follow-redirects@1.15.11: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + fs-extra@11.3.3: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + iconv-lite@0.7.1: + dependencies: + safer-buffer: 2.1.2 + + inherits@2.0.4: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + math-intrinsics@1.1.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + proxy-from-env@1.1.0: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.1 + unpipe: 1.0.0 + + safer-buffer@2.1.2: {} + + setprototypeof@1.2.0: {} + + statuses@2.0.2: {} + + toidentifier@1.0.1: {} + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + zod@3.25.76: {} From 3af9f4d5e4ec92205d2927d1bee89c1beeb36549 Mon Sep 17 00:00:00 2001 From: phin3has Date: Sat, 21 Feb 2026 17:32:17 -0700 Subject: [PATCH 07/13] fix: upgrade @modelcontextprotocol/sdk to 1.26.0 to patch high-severity vulns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes two high-severity vulnerabilities: - GHSA-8r9q-7v3j-jr4g: ReDoS in MCP TypeScript SDK (fixed in >=1.25.2) - GHSA-w48q-cv73-mx4w: Missing DNS rebinding protection (fixed in >=1.24.0) No code changes required — the SDK API is fully backwards-compatible. Co-Authored-By: Claude Sonnet 4.6 --- package-lock.json | 1004 +++++++++++++++++++++++++++++++++++++++++++-- package.json | 2 +- 2 files changed, 962 insertions(+), 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3334689b4..21519ff3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "godot-mcp", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "godot-mcp", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "0.6.0", + "@modelcontextprotocol/sdk": "^1.26.0", "axios": "^1.7.9", "fs-extra": "^11.2.0" }, @@ -19,17 +19,61 @@ "devDependencies": { "@types/node": "^20.11.24", "typescript": "^5.3.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.0.tgz", - "integrity": "sha512-9rsDudGhDtMbvxohPoMMyAUOmEzQsOK+XFchh6gZGqo8sx9sBuZQs+CUttXqa8RZXKDaJRCN2tUtgGof7jRkkw==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", + "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", "license": "MIT", "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "zod": "^3.23.8" + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } } }, "node_modules/@types/node": { @@ -42,6 +86,77 @@ "undici-types": "~6.19.2" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -49,16 +164,40 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -81,6 +220,22 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -93,6 +248,19 @@ "node": ">= 0.8" } }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", @@ -102,6 +270,72 @@ "node": ">= 0.6" } }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -134,6 +368,21 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -179,10 +428,175 @@ "node": ">= 0.4" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", + "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "license": "MIT", + "dependencies": { + "ip-address": "10.0.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { "type": "individual", @@ -200,9 +614,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -215,6 +629,24 @@ "node": ">= 6" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fs-extra": { "version": "11.3.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", @@ -332,32 +764,49 @@ "node": ">= 0.4" } }, + "node_modules/hono": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.1.tgz", + "integrity": "sha512-hi9afu8g0lfJVLolxElAZGANCTTl6bewIdsRNhaywfP9K8BPf++F2z6OLrYGIinUwpRKzbZHMhPwvc0ZEpAwGw==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/inherits": { @@ -366,6 +815,57 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -387,6 +887,27 @@ "node": ">= 0.4" } }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -408,25 +929,181 @@ "node": ">= 0.6" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" } }, "node_modules/safer-buffer": { @@ -435,16 +1112,179 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -459,6 +1299,45 @@ "node": ">=0.6" } }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/typescript": { "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", @@ -498,14 +1377,53 @@ "node": ">= 0.8" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/zod": { - "version": "3.24.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", - "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } } } } diff --git a/package.json b/package.json index fe431bef8..6815dc984 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "watch": "tsc --watch" }, "dependencies": { - "@modelcontextprotocol/sdk": "0.6.0", + "@modelcontextprotocol/sdk": "^1.26.0", "axios": "^1.7.9", "fs-extra": "^11.2.0" }, From 3139d2953ee68e985e5986e2ef3efc8f080f8aa7 Mon Sep 17 00:00:00 2001 From: Zach Merrill <6911696+zachmerrill@users.noreply.github.com> Date: Sat, 28 Feb 2026 12:41:41 -0500 Subject: [PATCH 08/13] feat: add mcpb build and instructions --- .gitignore | 1 + README.md | 21 +- icon_color.png | Bin 0 -> 35761 bytes manifest.json | 59 ++ package-lock.json | 1759 ++++++++++++++++++++++------------------- package.json | 5 +- scripts/build-mcpb.js | 61 ++ 7 files changed, 1104 insertions(+), 802 deletions(-) create mode 100644 icon_color.png create mode 100644 manifest.json create mode 100644 scripts/build-mcpb.js diff --git a/.gitignore b/.gitignore index bd7fd82c1..69aaf3f74 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules/ # Build output build/ dist/ +mcpb-staging/ # Logs *.log diff --git a/README.md b/README.md index 83fe75f58..9b39f5bd1 100644 --- a/README.md +++ b/README.md @@ -99,15 +99,34 @@ This direct feedback loop helps AI assistants like Claude understand what works ### Step 1: Install and Build -First, clone the repository and build the MCP server: + +First, clone the repository and install dependencies: ```bash git clone https://github.com/Coding-Solo/godot-mcp.git cd godot-mcp npm install +``` + +#### Node + +To build the MCP server for CLI use: + +```bash npm run build ``` +#### Claude Desktop (MCPB) + +To build the MCPB extension for Claude Desktop: + +```bash +npm run build:mcpb +``` + +After building, double-click the generated `.mcpb` file in the build directory to install it in Claude Desktop. In the Config menu, set the path to your Godot executable. You can skip the rest of the configuration steps below. + + ### Step 2: Configure with Your AI Assistant #### Option A: Configure with Cline diff --git a/icon_color.png b/icon_color.png new file mode 100644 index 0000000000000000000000000000000000000000..0e204bc49c50fe76a90460928387a9043a3a08f5 GIT binary patch literal 35761 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 literal 0 HcmV?d00001 diff --git a/manifest.json b/manifest.json new file mode 100644 index 000000000..d4c716646 --- /dev/null +++ b/manifest.json @@ -0,0 +1,59 @@ +{ + "manifest_version": "0.3", + "name": "Godot MCP", + "version": "0.0.0", + "description": "MCP server for interfacing with Godot game engine. Launch editor, run projects, manage scenes, and capture debug output.", + "author": { + "name": "Solomon Elias", + "email": "solomon@sippy.ai" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Coding-Solo/godot-mcp" + }, + "homepage": "https://github.com/Coding-Solo/godot-mcp", + "icon": "icon_color.png", + "server": { + "type": "node", + "entry_point": "server/index.js", + "mcp_config": { + "command": "node", + "args": ["${__dirname}/server/index.js"], + "env": { + "GODOT_PATH": "${user_config.godot_path}" + } + } + }, + "user_config": { + "godot_path": { + "type": "file", + "title": "Godot Executable Path", + "description": "Path to the Godot engine executable. Leave empty for auto-detection from system PATH.", + "required": false + } + }, + "compatibility": { + "platforms": ["darwin", "win32", "linux"], + "runtimes": { + "node": ">=18.0.0" + } + }, + "tools": [ + { "name": "launch_editor", "description": "Open Godot editor for a project" }, + { "name": "run_project", "description": "Execute Godot project in debug mode" }, + { "name": "get_debug_output", "description": "Retrieve captured console output" }, + { "name": "stop_project", "description": "Terminate running Godot process" }, + { "name": "get_godot_version", "description": "Get installed Godot version" }, + { "name": "list_projects", "description": "Find Godot projects in a directory" }, + { "name": "get_project_info", "description": "Get project metadata and structure" }, + { "name": "create_scene", "description": "Create new scene files" }, + { "name": "add_node", "description": "Add nodes to existing scenes" }, + { "name": "load_sprite", "description": "Load textures into Sprite2D nodes" }, + { "name": "export_mesh_library", "description": "Export 3D scenes as MeshLibrary" }, + { "name": "save_scene", "description": "Save scene changes" }, + { "name": "get_uid", "description": "Get UID for a specific file (Godot 4.4+)" }, + { "name": "update_project_uids", "description": "Update UID references across project resources" } + ], + "keywords": ["godot", "game-engine", "game-development"] +} diff --git a/package-lock.json b/package-lock.json index 21519ff3d..b0f6d886b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.1", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.26.0", + "@modelcontextprotocol/sdk": "0.6.0", "axios": "^1.7.9", "fs-extra": "^11.2.0" }, @@ -17,185 +17,743 @@ "godot-mcp": "build/index.js" }, "devDependencies": { + "@anthropic-ai/mcpb": "^2.1.2", "@types/node": "^20.11.24", + "esbuild": "^0.18.20", "typescript": "^5.3.3" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@hono/node-server": { - "version": "1.19.9", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", - "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "node_modules/@anthropic-ai/mcpb": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@anthropic-ai/mcpb/-/mcpb-2.1.2.tgz", + "integrity": "sha512-goRbBC8ySo7SWb7tRzr+tL6FxDc4JPTRCdgfD2omba7freofvjq5rom1lBnYHZHo6Mizs1jAHJeN53aZbDoy8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/prompts": "^6.0.1", + "commander": "^13.1.0", + "fflate": "^0.8.2", + "galactus": "^1.0.0", + "ignore": "^7.0.5", + "node-forge": "^1.3.2", + "pretty-bytes": "^5.6.0", + "zod": "^3.25.67", + "zod-to-json-schema": "^3.24.6" + }, + "bin": { + "mcpb": "dist/cli/cli.js" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.14.1" + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", + "integrity": "sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" }, - "peerDependencies": { - "hono": "^4" + "engines": { + "node": ">=18" } }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", - "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", + "node_modules/@inquirer/confirm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-4.0.1.tgz", + "integrity": "sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==", + "dev": true, "license": "MIT", "dependencies": { - "@hono/node-server": "^1.19.9", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.2.1", - "express-rate-limit": "^8.2.1", - "hono": "^4.11.4", - "jose": "^6.1.3", - "json-schema-typed": "^8.0.2", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.1" + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" }, "engines": { "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@types/node": { + "version": "22.19.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.13.tgz", + "integrity": "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@inquirer/core/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/editor": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-3.0.1.tgz", + "integrity": "sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "external-editor": "^3.1.0" }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } + "engines": { + "node": ">=18" } }, - "node_modules/@types/node": { - "version": "20.17.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.24.tgz", - "integrity": "sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA==", + "node_modules/@inquirer/expand": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-3.0.1.tgz", + "integrity": "sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" } }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-3.0.1.tgz", + "integrity": "sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==", + "dev": true, "license": "MIT", "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/accepts/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/@inquirer/number": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-2.0.1.tgz", + "integrity": "sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==", + "dev": true, "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/accepts/node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "node_modules/@inquirer/password": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-3.0.1.tgz", + "integrity": "sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==", + "dev": true, "license": "MIT", "dependencies": { - "mime-db": "^1.54.0" + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2" }, "engines": { "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-6.0.1.tgz", + "integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^3.0.1", + "@inquirer/confirm": "^4.0.1", + "@inquirer/editor": "^3.0.1", + "@inquirer/expand": "^3.0.1", + "@inquirer/input": "^3.0.1", + "@inquirer/number": "^2.0.1", + "@inquirer/password": "^3.0.1", + "@inquirer/rawlist": "^3.0.1", + "@inquirer/search": "^2.0.1", + "@inquirer/select": "^3.0.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "engines": { + "node": ">=18" } }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "node_modules/@inquirer/rawlist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-3.0.1.tgz", + "integrity": "sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==", + "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=18" } }, - "node_modules/ajv-formats": { + "node_modules/@inquirer/search": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-2.0.1.tgz", + "integrity": "sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-3.0.1.tgz", + "integrity": "sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==", + "dev": true, "license": "MIT", "dependencies": { - "ajv": "^8.0.0" + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" }, - "peerDependencies": { - "ajv": "^8.0.0" + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "engines": { + "node": ">=18" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "node_modules/@modelcontextprotocol/sdk": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.0.tgz", + "integrity": "sha512-9rsDudGhDtMbvxohPoMMyAUOmEzQsOK+XFchh6gZGqo8sx9sBuZQs+CUttXqa8RZXKDaJRCN2tUtgGof7jRkkw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "raw-body": "^3.0.0", + "zod": "^3.23.8" + } + }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.17.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.24.tgz", + "integrity": "sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true, "license": "MIT" }, - "node_modules/axios": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", - "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.11", - "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", - "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=18" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" } }, "node_modules/bytes": { @@ -220,22 +778,43 @@ "node": ">= 0.4" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=7.0.0" } }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -248,17 +827,14 @@ "node": ">= 0.8" } }, - "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/content-type": { @@ -270,59 +846,11 @@ "node": ">= 0.6" } }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -368,21 +896,13 @@ "node": ">= 0.4" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -428,175 +948,112 @@ "node": ">= 0.4" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventsource": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", - "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "eventsource-parser": "^3.0.1" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eventsource-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", - "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, "license": "MIT", "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=4" } }, - "node_modules/express-rate-limit": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", - "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, "license": "MIT", "dependencies": { - "ip-address": "10.0.1" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": ">= 4.11" + "node": ">=0.10.0" } }, - "node_modules/express/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" }, - "node_modules/express/node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "node_modules/flora-colossus": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz", + "integrity": "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==", + "dev": true, "license": "MIT", "dependencies": { - "mime-db": "^1.54.0" + "debug": "^4.3.4", + "fs-extra": "^10.1.0" }, "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 12" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "node_modules/flora-colossus/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=12" } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -614,9 +1071,9 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -629,24 +1086,6 @@ "node": ">= 6" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/fs-extra": { "version": "11.3.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", @@ -670,6 +1109,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/galactus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz", + "integrity": "sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "flora-colossus": "^2.0.0", + "fs-extra": "^10.1.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/galactus/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -758,55 +1227,48 @@ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hono": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.1.tgz", - "integrity": "sha512-hi9afu8g0lfJVLolxElAZGANCTTl6bewIdsRNhaywfP9K8BPf++F2z6OLrYGIinUwpRKzbZHMhPwvc0ZEpAwGw==", - "license": "MIT", + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=16.9.0" + "node": ">= 0.4" } }, "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "license": "MIT", "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" } }, "node_modules/inherits": { @@ -815,57 +1277,16 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, - "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/jose": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", - "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" + "node": ">=8" } }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause" - }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -887,27 +1308,6 @@ "node": ">= 0.4" } }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -933,107 +1333,50 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, - "node_modules/negotiator": { + "node_modules/mute-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, "license": "ISC", - "dependencies": { - "wrappy": "1" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/parseurl": { + "node_modules/node-forge": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { - "node": ">= 0.8" + "node": ">= 6.13.0" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=0.10.0" } }, - "node_modules/pkce-challenge": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", - "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, "license": "MIT", "engines": { - "node": ">=16.20.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "node": ">=6" }, - "engines": { - "node": ">= 0.10" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/proxy-from-env": { @@ -1042,68 +1385,19 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, - "node_modules/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 0.8" } }, "node_modules/safer-buffer": { @@ -1112,182 +1406,73 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/send/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, "engines": { - "node": ">= 0.8" + "node": ">=0.6.0" } }, "node_modules/toidentifier": { @@ -1299,43 +1484,17 @@ "node": ">=0.6" } }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=18" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typescript": { @@ -1377,41 +1536,40 @@ "node": ">= 0.8" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1420,6 +1578,7 @@ "version": "3.25.1", "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "dev": true, "license": "ISC", "peerDependencies": { "zod": "^3.25 || ^4" diff --git a/package.json b/package.json index 6815dc984..86aa43ff9 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ ], "scripts": { "build": "tsc && node scripts/build.js", + "build:mcpb": "npm run build && node scripts/build-mcpb.js", "inspector": "npx @modelcontextprotocol/inspector build/index.js", "prepare": "npm run build", "watch": "tsc --watch" @@ -38,8 +39,10 @@ "fs-extra": "^11.2.0" }, "devDependencies": { + "@anthropic-ai/mcpb": "^2.1.2", "@types/node": "^20.11.24", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "esbuild": "^0.18.20" }, "engines": { "node": ">=18.0.0" diff --git a/scripts/build-mcpb.js b/scripts/build-mcpb.js new file mode 100644 index 000000000..73670a84e --- /dev/null +++ b/scripts/build-mcpb.js @@ -0,0 +1,61 @@ +import * as esbuild from 'esbuild'; +import { packExtension } from '@anthropic-ai/mcpb'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const root = path.join(__dirname, '..'); +const stagingDir = path.join(root, 'mcpb-staging'); + +const buildIndex = path.join(root, 'build', 'index.js'); +const buildGd = path.join(root, 'build', 'scripts', 'godot_operations.gd'); + +if (!fs.existsSync(buildIndex)) { + console.error('Error: build/index.js not found. Run "npm run build" first.'); + process.exit(1); +} +if (!fs.existsSync(buildGd)) { + console.error('Error: build/scripts/godot_operations.gd not found. Run "npm run build" first.'); + process.exit(1); +} + +fs.rmSync(stagingDir, { recursive: true, force: true }); +fs.mkdirSync(path.join(stagingDir, 'server', 'scripts'), { recursive: true }); + +try { + const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')); + const manifest = JSON.parse(fs.readFileSync(path.join(root, 'manifest.json'), 'utf8')); + manifest.version = pkg.version; + fs.writeFileSync(path.join(stagingDir, 'manifest.json'), JSON.stringify(manifest, null, 2)); + console.log(`Manifest version: ${pkg.version}`); + + console.log('Bundling with esbuild...'); + await esbuild.build({ + entryPoints: [buildIndex], + bundle: true, + platform: 'node', + target: 'node18', + format: 'esm', + outfile: path.join(stagingDir, 'server', 'index.js'), + }); + + fs.copyFileSync(buildGd, path.join(stagingDir, 'server', 'scripts', 'godot_operations.gd')); + + const iconSrc = path.join(root, 'icon_color.png'); + if (fs.existsSync(iconSrc)) { + fs.copyFileSync(iconSrc, path.join(stagingDir, 'icon_color.png')); + console.log('Copied icon_color.png'); + } else { + console.warn('Warning: icon_color.png not found'); + } + + fs.mkdirSync(path.join(root, 'build'), { recursive: true }); + const outputFile = path.join(root, 'build', 'godot-mcp.mcpb'); + console.log('Packing MCPB...'); + const success = await packExtension({ extensionPath: stagingDir, outputPath: outputFile }); + if (!success) process.exit(1); + console.log('\nMCPB bundle created: build/godot-mcp.mcpb'); +} finally { + fs.rmSync(stagingDir, { recursive: true, force: true }); +} From 32f6c521ed47a4d39be97041244e88e1749f5181 Mon Sep 17 00:00:00 2001 From: Zach Merrill <6911696+zachmerrill@users.noreply.github.com> Date: Sat, 28 Feb 2026 12:43:59 -0500 Subject: [PATCH 09/13] chore: update esbuild --- package-lock.json | 302 ++++++++++++++++++++++++++++------------------ package.json | 2 +- 2 files changed, 188 insertions(+), 116 deletions(-) diff --git a/package-lock.json b/package-lock.json index b0f6d886b..a7e4073d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "devDependencies": { "@anthropic-ai/mcpb": "^2.1.2", "@types/node": "^20.11.24", - "esbuild": "^0.18.20", + "esbuild": "^0.27.3", "typescript": "^5.3.3" }, "engines": { @@ -47,10 +47,27 @@ "mcpb": "dist/cli/cli.js" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", "cpu": [ "arm" ], @@ -61,13 +78,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", "cpu": [ "arm64" ], @@ -78,13 +95,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", "cpu": [ "x64" ], @@ -95,13 +112,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", "cpu": [ "arm64" ], @@ -112,13 +129,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", "cpu": [ "x64" ], @@ -129,13 +146,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", "cpu": [ "arm64" ], @@ -146,13 +163,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", "cpu": [ "x64" ], @@ -163,13 +180,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", "cpu": [ "arm" ], @@ -180,13 +197,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", "cpu": [ "arm64" ], @@ -197,13 +214,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", "cpu": [ "ia32" ], @@ -214,13 +231,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", "cpu": [ "loong64" ], @@ -231,13 +248,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", "cpu": [ "mips64el" ], @@ -248,13 +265,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", "cpu": [ "ppc64" ], @@ -265,13 +282,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", "cpu": [ "riscv64" ], @@ -282,13 +299,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", "cpu": [ "s390x" ], @@ -299,13 +316,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", "cpu": [ "x64" ], @@ -316,13 +333,30 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", "cpu": [ "x64" ], @@ -333,13 +367,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", "cpu": [ "x64" ], @@ -350,13 +401,30 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", "cpu": [ "x64" ], @@ -367,13 +435,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", "cpu": [ "arm64" ], @@ -384,13 +452,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", "cpu": [ "ia32" ], @@ -401,13 +469,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", "cpu": [ "x64" ], @@ -418,7 +486,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@inquirer/checkbox": { @@ -949,9 +1017,9 @@ } }, "node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -959,31 +1027,35 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" } }, "node_modules/external-editor": { diff --git a/package.json b/package.json index 86aa43ff9..ebd37eef2 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@anthropic-ai/mcpb": "^2.1.2", "@types/node": "^20.11.24", "typescript": "^5.3.3", - "esbuild": "^0.18.20" + "esbuild": "^0.27.3" }, "engines": { "node": ">=18.0.0" From f78731ebd72de77550dd4af5c4bef9320d4c398e Mon Sep 17 00:00:00 2001 From: "qqhyyy.com" <60999@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:18:52 +0800 Subject: [PATCH 10/13] feat: Add capture_screenshot tool for multi-instance support - Add SCREENSHOT_WRAPPER_SCRIPT and SCREENSHOT_WRAPPER_SCENE constants - Add setupScreenshotWrapper, cleanupScreenshotArtifacts, getMainScenePath helper methods - Update GodotProcess interface with screenshotEnabled property - Update handleRunProject to support enableScreenshot parameter - Add capture_screenshot tool definition and handleCaptureScreenshot handler - Update cleanup method to clean screenshot artifacts on shutdown --- src/index.ts | 301 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 300 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index b55285b8e..35b13dbd2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,6 +46,7 @@ interface GodotProcess { startedAt: Date; isRunning: boolean; exitedAt?: Date; + screenshotEnabled?: boolean; } /** @@ -65,6 +66,47 @@ interface InstanceConfig { const OUTPUT_BUFFER_LIMIT = 1000; // Max lines to keep per stream const STALE_PROCESS_CLEANUP_MS = 10 * 60 * 1000; // 10 minutes +/** + * Screenshot wrapper script for capturing viewport + */ +const SCREENSHOT_WRAPPER_SCRIPT = `extends Node + +const REAL_MAIN_SCENE = "REPLACE_WITH_MAIN_SCENE_PATH" +const TRIGGER_FILE = "res://.mcp_screenshot_req" +const OUTPUT_FILE = "res://.mcp_screenshot.png" + +func _ready(): + print("[MCP] Wrapper loaded. Starting main scene: ", REAL_MAIN_SCENE) + if ResourceLoader.exists(REAL_MAIN_SCENE): + var main_packed = load(REAL_MAIN_SCENE) + var main_inst = main_packed.instantiate() + add_child.call_deferred(main_inst) + else: + printerr("[MCP] Error: Could not find main scene at ", REAL_MAIN_SCENE) + +func _process(_delta): + if FileAccess.file_exists(TRIGGER_FILE): + _take_screenshot() + DirAccess.remove_absolute(TRIGGER_FILE) + +func _take_screenshot(): + print("[MCP] Capturing screenshot...") + var image = get_viewport().get_texture().get_image() + var error = image.save_png(OUTPUT_FILE) + if error == OK: + print("[MCP] Screenshot saved to ", OUTPUT_FILE) + else: + printerr("[MCP] Failed to save screenshot. Error code: ", error) +`; + +const SCREENSHOT_WRAPPER_SCENE = `[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://.mcp_wrapper.gd" id="1_mcp"] + +[node name="MCPWrapper" type="Node"] +script = ExtResource("1_mcp") +`; + /** * Interface for server configuration */ @@ -465,6 +507,7 @@ class GodotServer { for (const [id, godotProcess] of this.activeProcesses.entries()) { this.logDebug(`Killing Godot process: ${id}`); try { + this.cleanupScreenshotArtifacts(godotProcess.projectPath); godotProcess.process.kill(); } catch (error) { this.logDebug(`Error killing process ${id}: ${error}`); @@ -474,6 +517,76 @@ class GodotServer { await this.server.close(); } + /** + * Setup screenshot wrapper files for a project + * @param projectPath Path to the Godot project + * @param mainScenePath Path to the main scene (relative to project) + */ + private setupScreenshotWrapper(projectPath: string, mainScenePath: string): void { + const fs = require('fs'); + const wrapperScriptPath = join(projectPath, '.mcp_wrapper.gd'); + const wrapperScenePath = join(projectPath, '.mcp_wrapper.tscn'); + + // Create wrapper script with the main scene path + const scriptContent = SCREENSHOT_WRAPPER_SCRIPT.replace( + 'REPLACE_WITH_MAIN_SCENE_PATH', + mainScenePath + ); + + fs.writeFileSync(wrapperScriptPath, scriptContent, 'utf8'); + fs.writeFileSync(wrapperScenePath, SCREENSHOT_WRAPPER_SCENE, 'utf8'); + + this.logDebug(`Created screenshot wrapper files in ${projectPath}`); + } + + /** + * Clean up screenshot wrapper files from a project + * @param projectPath Path to the Godot project + */ + private cleanupScreenshotArtifacts(projectPath: string): void { + const fs = require('fs'); + const filesToRemove = [ + '.mcp_wrapper.gd', + '.mcp_wrapper.tscn', + '.mcp_screenshot_req', + '.mcp_screenshot.png' + ]; + + for (const file of filesToRemove) { + const filePath = join(projectPath, file); + try { + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + this.logDebug(`Removed screenshot artifact: ${file}`); + } + } catch (error) { + this.logDebug(`Failed to remove ${file}: ${error}`); + } + } + } + + /** + * Get the main scene path from project.godot + * @param projectPath Path to the Godot project + * @returns The main scene path or null if not found + */ + private getMainScenePath(projectPath: string): string | null { + const fs = require('fs'); + const projectFile = join(projectPath, 'project.godot'); + + try { + const content = fs.readFileSync(projectFile, 'utf8'); + const match = content.match(/run\/main_scene="([^"]+)"/); + if (match && match[1]) { + return match[1]; + } + } catch (error) { + this.logDebug(`Failed to read project.godot: ${error}`); + } + + return null; + } + /** * Check if the Godot version is 4.4 or later * @param version The Godot version string @@ -779,6 +892,10 @@ class GodotServer { }, description: 'Optional: Additional command-line arguments to pass to Godot (e.g., ["--server"] or ["--", "profile=player1"])', }, + enableScreenshot: { + type: 'boolean', + description: 'Optional: Enable screenshot capture for this instance. When true, a wrapper scene will be used that allows capturing screenshots via capture_screenshot tool.', + }, }, required: ['projectPath'], }, @@ -816,6 +933,42 @@ class GodotServer { required: [], }, }, + { + name: 'capture_screenshot', + description: 'Capture a screenshot from a running Godot project instance. The instance must have been started with enableScreenshot=true.', + inputSchema: { + type: 'object', + properties: { + instanceId: { + type: 'string', + description: 'Instance ID of the running Godot project to capture screenshot from.', + }, + outputPath: { + type: 'string', + description: 'Optional: Path to save the screenshot. If not provided, returns base64 encoded image.', + }, + }, + required: ['instanceId'], + }, + }, + { + name: 'capture_screenshot', + description: 'Capture a screenshot from a running Godot project instance. The instance must have been started with enableScreenshot=true.', + inputSchema: { + type: 'object', + properties: { + instanceId: { + type: 'string', + description: 'Instance ID of the running Godot project to capture screenshot from.', + }, + outputPath: { + type: 'string', + description: 'Optional: Path to save the screenshot. If not provided, returns base64 encoded image.', + }, + }, + required: ['instanceId'], + }, + }, { name: 'get_godot_version', description: 'Get the installed Godot version', @@ -1087,6 +1240,8 @@ class GodotServer { return await this.handleGetDebugOutput(request.params.arguments); case 'stop_project': return await this.handleStopProject(request.params.arguments); + case 'capture_screenshot': + return await this.handleCaptureScreenshot(request.params.arguments); case 'get_godot_version': return await this.handleGetGodotVersion(); case 'list_projects': @@ -1260,7 +1415,22 @@ class GodotServer { } const cmdArgs = ['-d', '--path', args.projectPath]; - if (args.scene && this.validatePath(args.scene)) { + + // Handle screenshot wrapper setup + let screenshotEnabled = false; + if (args.enableScreenshot === true) { + const mainScene = this.getMainScenePath(args.projectPath); + if (mainScene) { + this.setupScreenshotWrapper(args.projectPath, mainScene); + cmdArgs.push('.mcp_wrapper.tscn'); + screenshotEnabled = true; + this.logDebug(`Screenshot wrapper enabled for instance ${instanceId}`); + } else { + this.logDebug(`Could not determine main scene, screenshot disabled for instance ${instanceId}`); + } + } + + if (args.scene && this.validatePath(args.scene) && !screenshotEnabled) { this.logDebug(`Adding scene parameter: ${args.scene}`); cmdArgs.push(args.scene); } @@ -1289,6 +1459,7 @@ class GodotServer { args: args.args, startedAt: new Date(), isRunning: true, + screenshotEnabled, }; godotProcess.stdout?.on('data', (data: Buffer) => { @@ -1634,6 +1805,134 @@ class GodotServer { }; } + /** + * Handle the capture_screenshot tool + * Captures a screenshot from a running Godot project instance + */ + private async handleCaptureScreenshot(args: any) { + const fs = require('fs'); + args = this.normalizeParameters(args || {}); + + if (!args.instanceId) { + return this.createErrorResponse( + 'instanceId is required', + ['Provide the instance ID of the running Godot project'] + ); + } + + const process = this.activeProcesses.get(args.instanceId); + if (!process) { + return this.createErrorResponse( + `No process found with instanceId: ${args.instanceId}`, + [ + 'Use list_processes to see all tracked instances', + 'The instance may have been cleaned up after being exited for too long', + ] + ); + } + + if (!process.isRunning) { + return this.createErrorResponse( + `Instance ${args.instanceId} is not running`, + ['The Godot project must be running to capture a screenshot'] + ); + } + + if (!process.screenshotEnabled) { + return this.createErrorResponse( + `Instance ${args.instanceId} does not have screenshot support enabled`, + [ + 'Restart the instance with enableScreenshot=true', + 'Use run_project with enableScreenshot parameter to enable screenshot capture', + ] + ); + } + + try { + // Create trigger file to request screenshot + const triggerPath = join(process.projectPath, '.mcp_screenshot_req'); + const outputPath = join(process.projectPath, '.mcp_screenshot.png'); + + // Remove any existing screenshot + if (fs.existsSync(outputPath)) { + fs.unlinkSync(outputPath); + } + + // Create trigger file + fs.writeFileSync(triggerPath, '', 'utf8'); + this.logDebug(`Created screenshot trigger file for instance ${args.instanceId}`); + + // Wait for screenshot to be captured (with timeout) + const maxWaitMs = 5000; + const checkIntervalMs = 100; + let elapsed = 0; + + while (elapsed < maxWaitMs) { + await new Promise(resolve => setTimeout(resolve, checkIntervalMs)); + elapsed += checkIntervalMs; + + if (fs.existsSync(outputPath)) { + this.logDebug(`Screenshot captured for instance ${args.instanceId}`); + + // Read the screenshot file + const imageBuffer = fs.readFileSync(outputPath); + const base64Image = imageBuffer.toString('base64'); + + // If outputPath is provided, save to that location + if (args.outputPath && this.validatePath(args.outputPath)) { + fs.copyFileSync(outputPath, args.outputPath); + return { + content: [ + { + type: 'text', + text: JSON.stringify( + { + success: true, + message: `Screenshot saved to ${args.outputPath}`, + instanceId: args.instanceId, + savedPath: args.outputPath, + }, + null, + 2 + ), + }, + ], + }; + } + + // Return base64 encoded image + return { + content: [ + { + type: 'image', + data: base64Image, + mimeType: 'image/png', + }, + ], + }; + } + } + + return this.createErrorResponse( + 'Screenshot capture timed out', + [ + 'The Godot project may not be responding', + 'Ensure the game loop is running (not paused or blocked)', + 'Try again after the game has fully loaded', + ] + ); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + return this.createErrorResponse( + `Failed to capture screenshot: ${errorMessage}`, + [ + 'Ensure the Godot project is running correctly', + 'Check if the project has write permissions', + ] + ); + } + } + /** * Handle the run_multiple_projects tool * Launches multiple Godot instances with optional staggered delays From e0e1c1ec3cb76e8509ba3c9765048fbe412cc80b Mon Sep 17 00:00:00 2001 From: "qqhyyy.com" <60999@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:44:52 +0800 Subject: [PATCH 11/13] feat: Add HTTP transport, i18n support, config wizard, signal/group/UI tools - Add HTTP transport mode (MCP_TRANSPORT=http, MCP_HTTP_PORT=3000) - Add i18n module supporting 10 languages (en, zh-CN, zh-TW, ja, etc.) - Add configuration wizard script (scripts/configure.js) - Add signal tools: signal_connect, signal_disconnect - Add group tools: group_add, group_remove, list_groups - Add UI tools: ui_create_button, ui_create_label --- scripts/configure.js | 305 +++++++++++++++++++++++++++ src/i18n.ts | 485 +++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 395 ++++++++++++++++++++++++++++++++++- 3 files changed, 1182 insertions(+), 3 deletions(-) create mode 100644 scripts/configure.js create mode 100644 src/i18n.ts diff --git a/scripts/configure.js b/scripts/configure.js new file mode 100644 index 000000000..95a5f7252 --- /dev/null +++ b/scripts/configure.js @@ -0,0 +1,305 @@ +#!/usr/bin/env node +/** + * MCP Configuration Wizard + * Automatically generates MCP configuration files for various AI clients + */ + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +/** + * Supported AI clients configuration + */ +const CLIENTS = { + // IDE clients + 'trae-cn': { + name: 'Trae CN', + type: 'ide', + configPaths: { + win32: path.join(process.env.APPDATA || '', 'Trae CN', 'User', 'mcp.json'), + darwin: path.join(os.homedir(), 'Library', 'Application Support', 'Trae CN', 'User', 'mcp.json'), + linux: path.join(os.homedir(), '.config', 'Trae CN', 'User', 'mcp.json'), + }, + }, + 'cursor': { + name: 'Cursor', + type: 'ide', + configPaths: { + win32: path.join(process.env.APPDATA || '', 'Cursor', 'User', 'mcp.json'), + darwin: path.join(os.homedir(), '.cursor', 'mcp.json'), + linux: path.join(os.homedir(), '.cursor', 'mcp.json'), + }, + }, + 'windsurf': { + name: 'Windsurf', + type: 'ide', + configPaths: { + win32: path.join(process.env.APPDATA || '', 'Codeium', 'windsurf', 'mcp_config.json'), + darwin: path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'), + linux: path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'), + }, + }, + 'claude-desktop': { + name: 'Claude Desktop', + type: 'ide', + configPaths: { + win32: path.join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json'), + darwin: path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'), + linux: path.join(os.homedir(), '.config', 'Claude', 'claude_desktop_config.json'), + }, + }, + 'cline': { + name: 'Cline (VS Code)', + type: 'ide', + configPaths: { + win32: path.join(process.env.APPDATA || '', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'), + darwin: path.join(os.homedir(), 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'), + linux: path.join(os.homedir(), '.config', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'), + }, + }, +}; + +/** + * CLI commands for different CLI tools + */ +const CLI_COMMANDS = { + 'claude-cli': { + name: 'Claude CLI (Claude Code)', + command: (serverPath, scope = 'user') => + `claude mcp add --scope ${scope} --transport stdio godot-mcp node "${serverPath}"`, + }, + 'codex-cli': { + name: 'Codex CLI', + command: (serverPath, scope = 'user') => + `codex mcp add --scope ${scope} --transport stdio godot-mcp node "${serverPath}"`, + }, + 'gemini-cli': { + name: 'Gemini CLI', + command: (serverPath, scope = 'user') => + `gemini mcp add --scope ${scope} --transport stdio godot-mcp node "${serverPath}"`, + }, +}; + +/** + * Get the build output path + */ +function getServerPath() { + const scriptDir = __dirname; + return path.join(scriptDir, '..', 'build', 'index.js'); +} + +/** + * Generate MCP configuration for a client + */ +function generateConfig(serverPath, httpPort = null) { + const config = { + mcpServers: { + 'godot-mcp': { + command: 'node', + args: [serverPath], + env: { + DEBUG: 'false', + }, + }, + }, + }; + + if (httpPort) { + config.mcpServers['godot-mcp'].env.MCP_TRANSPORT = 'http'; + config.mcpServers['godot-mcp'].env.MCP_HTTP_PORT = httpPort.toString(); + } + + return config; +} + +/** + * Merge with existing configuration + */ +function mergeConfig(existingConfig, newConfig) { + if (!existingConfig) return newConfig; + + const merged = { ...existingConfig }; + merged.mcpServers = { + ...(existingConfig.mcpServers || {}), + ...newConfig.mcpServers, + }; + return merged; +} + +/** + * Configure a specific IDE client + */ +function configureClient(clientId, serverPath, httpPort = null) { + const client = CLIENTS[clientId]; + if (!client) { + console.error(`Unknown client: ${clientId}`); + console.log(`Available clients: ${Object.keys(CLIENTS).join(', ')}`); + return false; + } + + const configPath = client.configPaths[process.platform]; + if (!configPath) { + console.error(`Platform ${process.platform} is not supported for ${client.name}`); + return false; + } + + const newConfig = generateConfig(serverPath, httpPort); + + // Read existing config if it exists + let existingConfig = null; + if (fs.existsSync(configPath)) { + try { + const content = fs.readFileSync(configPath, 'utf8'); + existingConfig = JSON.parse(content); + console.log(`Found existing configuration at: ${configPath}`); + } catch (error) { + console.log(`Could not parse existing config, will create new one`); + } + } + + // Merge configurations + const finalConfig = mergeConfig(existingConfig, newConfig); + + // Ensure directory exists + const configDir = path.dirname(configPath); + if (!fs.existsSync(configDir)) { + fs.mkdirSync(configDir, { recursive: true }); + console.log(`Created directory: ${configDir}`); + } + + // Write configuration + fs.writeFileSync(configPath, JSON.stringify(finalConfig, null, 2), 'utf8'); + console.log(`✅ Configuration written to: ${configPath}`); + console.log(` Client: ${client.name}`); + console.log(` Server: ${serverPath}`); + + if (httpPort) { + console.log(` Transport: HTTP (port ${httpPort})`); + } else { + console.log(` Transport: stdio`); + } + + return true; +} + +/** + * Print CLI commands + */ +function printCliCommands(serverPath) { + console.log('\n📋 CLI Configuration Commands:\n'); + console.log('Copy and run these commands in your terminal:\n'); + + for (const [id, cli] of Object.entries(CLI_COMMANDS)) { + console.log(`# ${cli.name}`); + console.log(cli.command(serverPath, 'user')); + console.log(''); + } +} + +/** + * Print all available clients + */ +function printAvailableClients() { + console.log('\n📦 Available IDE Clients:\n'); + for (const [id, client] of Object.entries(CLIENTS)) { + const configPath = client.configPaths[process.platform]; + const exists = configPath && fs.existsSync(configPath); + const status = exists ? '✅ (config exists)' : '❌ (not configured)'; + console.log(` ${id.padEnd(15)} - ${client.name} ${status}`); + } + + console.log('\n📡 Available CLI Tools:\n'); + for (const [id, cli] of Object.entries(CLI_COMMANDS)) { + console.log(` ${id.padEnd(15)} - ${cli.name}`); + } +} + +/** + * Main function + */ +function main() { + const args = process.argv.slice(2); + const serverPath = getServerPath(); + + // Check if server build exists + if (!fs.existsSync(serverPath)) { + console.error('❌ Server build not found. Please run "npm run build" first.'); + process.exit(1); + } + + console.log('🔧 Godot MCP Configuration Wizard\n'); + console.log(`Server path: ${serverPath}`); + console.log(`Platform: ${process.platform}\n`); + + // Parse arguments + let targetClient = null; + let httpPort = null; + let showCli = false; + let listMode = false; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (arg === '--client' || arg === '-c') { + targetClient = args[++i]; + } else if (arg === '--http' || arg === '-h') { + httpPort = parseInt(args[++i]) || 3000; + } else if (arg === '--cli') { + showCli = true; + } else if (arg === '--list' || arg === '-l') { + listMode = true; + } else if (arg === '--help') { + console.log(` +Usage: node configure.js [options] + +Options: + --client, -c Configure a specific IDE client + --http, -h Use HTTP transport (default port: 3000) + --cli Show CLI configuration commands + --list, -l List all available clients + --help Show this help message + +Examples: + node configure.js --client cursor + node configure.js --client trae-cn --http 3000 + node configure.js --cli + node configure.js --list +`); + process.exit(0); + } + } + + // List mode + if (listMode) { + printAvailableClients(); + process.exit(0); + } + + // Show CLI commands + if (showCli) { + printCliCommands(serverPath); + process.exit(0); + } + + // Configure specific client + if (targetClient) { + configureClient(targetClient, serverPath, httpPort); + process.exit(0); + } + + // Interactive mode - configure all common clients + console.log('Configuring common IDE clients...\n'); + + const commonClients = ['trae-cn', 'cursor', 'claude-desktop']; + for (const clientId of commonClients) { + console.log(`\n--- Configuring ${CLIENTS[clientId]?.name || clientId} ---`); + configureClient(clientId, serverPath, httpPort); + } + + // Also show CLI commands + printCliCommands(serverPath); + + console.log('\n✨ Configuration complete! Restart your IDE to apply changes.\n'); +} + +main(); diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 000000000..838b8d793 --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,485 @@ +/** + * Internationalization (i18n) module for Godot MCP Server + * Supports multiple languages for error messages and UI text + */ + +/** + * Supported languages + */ +export type Language = 'en' | 'zh-CN' | 'zh-TW' | 'ja' | 'ko' | 'ru' | 'fr' | 'de' | 'es' | 'pt'; + +/** + * Message categories + */ +export type MessageCategory = 'errors' | 'warnings' | 'info' | 'tools' | 'descriptions'; + +/** + * Translation messages structure + */ +interface Messages { + errors: Record; + warnings: Record; + info: Record; + tools: Record; + descriptions: Record; +} + +/** + * English messages (default) + */ +const enMessages: Messages = { + errors: { + project_path_required: 'Project path is required', + invalid_project_path: 'Invalid project path', + not_godot_project: 'Not a valid Godot project: {path}', + godot_not_found: 'Could not find a valid Godot executable path', + no_active_processes: 'No active Godot processes', + instance_not_found: 'No process found with instanceId: {id}', + instance_not_running: 'Instance {id} is not running', + instance_already_running: 'Instance ID "{id}" is already in use by a running process', + screenshot_not_enabled: 'Instance {id} does not have screenshot support enabled', + screenshot_timeout: 'Screenshot capture timed out', + scene_not_found: 'Scene not found: {path}', + node_not_found: 'Node not found: {path}', + script_not_found: 'Script not found: {path}', + resource_not_found: 'Resource not found: {path}', + invalid_path: 'Invalid path: {path}', + file_write_error: 'Failed to write file: {path}', + file_read_error: 'Failed to read file: {path}', + operation_failed: 'Operation failed: {operation}', + signal_not_found: 'Signal not found: {signal}', + group_not_found: 'Group not found: {group}', + }, + warnings: { + process_cleanup: 'Cleaning up exited process {id} to allow ID reuse', + screenshot_disabled: 'Could not determine main scene, screenshot disabled for instance {id}', + deprecated_tool: 'Tool {tool} is deprecated, use {replacement} instead', + }, + info: { + server_started: 'Godot MCP Server started', + process_started: 'Godot project started with instanceId: {id}', + process_stopped: 'Godot project instance "{id}" stopped', + screenshot_captured: 'Screenshot captured for instance {id}', + scene_created: 'Scene created: {path}', + node_added: 'Node added: {name}', + signal_connected: 'Signal {signal} connected', + group_added: 'Node added to group: {group}', + }, + tools: { + launch_editor: 'Launch Godot editor for a specific project', + run_project: 'Run the Godot project and capture output', + get_debug_output: 'Get the current debug output and errors', + stop_project: 'Stop running Godot project instance(s)', + capture_screenshot: 'Capture a screenshot from a running Godot project instance', + get_godot_version: 'Get the installed Godot version', + list_projects: 'List Godot projects in a directory', + get_project_info: 'Retrieve metadata about a Godot project', + create_scene: 'Create a new scene', + add_node: 'Add a new node to a scene', + load_sprite: 'Load a sprite into a Sprite2D node', + save_scene: 'Save a scene', + signal_connect: 'Connect a signal from one node to another', + signal_disconnect: 'Disconnect a signal connection', + group_add: 'Add a node to a group', + group_remove: 'Remove a node from a group', + list_groups: 'List all groups in a scene', + }, + descriptions: { + projectPath: 'Path to the Godot project directory', + scene: 'Optional: Specific scene to run', + instanceId: 'Optional: Unique identifier for this instance', + args: 'Optional: Additional command-line arguments', + enableScreenshot: 'Optional: Enable screenshot capture for this instance', + outputPath: 'Optional: Path to save the output', + nodeName: 'Name of the node', + nodeType: 'Type of the node to create', + parentNodePath: 'Path to the parent node', + signalName: 'Name of the signal to connect', + sourceNode: 'Path to the source node (emitter)', + targetNode: 'Path to the target node (receiver)', + methodName: 'Name of the method to call when signal is emitted', + groupName: 'Name of the group', + }, +}; + +/** + * Simplified Chinese messages + */ +const zhCNMessages: Messages = { + errors: { + project_path_required: '项目路径是必需的', + invalid_project_path: '无效的项目路径', + not_godot_project: '不是有效的 Godot 项目: {path}', + godot_not_found: '找不到有效的 Godot 可执行文件路径', + no_active_processes: '没有活动的 Godot 进程', + instance_not_found: '找不到实例 ID: {id}', + instance_not_running: '实例 {id} 未运行', + instance_already_running: '实例 ID "{id}" 已被运行中的进程使用', + screenshot_not_enabled: '实例 {id} 未启用截图支持', + screenshot_timeout: '截图捕获超时', + scene_not_found: '找不到场景: {path}', + node_not_found: '找不到节点: {path}', + script_not_found: '找不到脚本: {path}', + resource_not_found: '找不到资源: {path}', + invalid_path: '无效路径: {path}', + file_write_error: '写入文件失败: {path}', + file_read_error: '读取文件失败: {path}', + operation_failed: '操作失败: {operation}', + signal_not_found: '找不到信号: {signal}', + group_not_found: '找不到分组: {group}', + }, + warnings: { + process_cleanup: '正在清理已退出的进程 {id} 以允许 ID 重用', + screenshot_disabled: '无法确定主场景,实例 {id} 的截图功能已禁用', + deprecated_tool: '工具 {tool} 已弃用,请使用 {replacement}', + }, + info: { + server_started: 'Godot MCP 服务器已启动', + process_started: 'Godot 项目已启动,实例 ID: {id}', + process_stopped: 'Godot 项目实例 "{id}" 已停止', + screenshot_captured: '实例 {id} 截图已捕获', + scene_created: '场景已创建: {path}', + node_added: '节点已添加: {name}', + signal_connected: '信号 {signal} 已连接', + group_added: '节点已添加到分组: {group}', + }, + tools: { + launch_editor: '启动指定项目的 Godot 编辑器', + run_project: '运行 Godot 项目并捕获输出', + get_debug_output: '获取当前的调试输出和错误', + stop_project: '停止运行中的 Godot 项目实例', + capture_screenshot: '从运行中的 Godot 项目实例捕获截图', + get_godot_version: '获取已安装的 Godot 版本', + list_projects: '列出目录中的 Godot 项目', + get_project_info: '获取 Godot 项目的元数据', + create_scene: '创建新场景', + add_node: '向场景添加新节点', + load_sprite: '加载精灵到 Sprite2D 节点', + save_scene: '保存场景', + signal_connect: '将信号从一个节点连接到另一个节点', + signal_disconnect: '断开信号连接', + group_add: '将节点添加到分组', + group_remove: '从分组中移除节点', + list_groups: '列出场景中的所有分组', + }, + descriptions: { + projectPath: 'Godot 项目目录的路径', + scene: '可选:要运行的特定场景', + instanceId: '可选:此实例的唯一标识符', + args: '可选:额外的命令行参数', + enableScreenshot: '可选:为此实例启用截图捕获', + outputPath: '可选:保存输出的路径', + nodeName: '节点名称', + nodeType: '要创建的节点类型', + parentNodePath: '父节点的路径', + signalName: '要连接的信号名称', + sourceNode: '源节点(发射器)的路径', + targetNode: '目标节点(接收器)的路径', + methodName: '信号发射时要调用的方法名称', + groupName: '分组名称', + }, +}; + +/** + * Traditional Chinese messages + */ +const zhTWMessages: Messages = { + errors: { + project_path_required: '專案路徑是必需的', + invalid_project_path: '無效的專案路徑', + not_godot_project: '不是有效的 Godot 專案: {path}', + godot_not_found: '找不到有效的 Godot 可執行檔路徑', + no_active_processes: '沒有活動的 Godot 进程', + instance_not_found: '找不到實例 ID: {id}', + instance_not_running: '實例 {id} 未運行', + instance_already_running: '實例 ID "{id}" 已被運行中的进程使用', + screenshot_not_enabled: '實例 {id} 未啟用截圖支援', + screenshot_timeout: '截圖捕獲超時', + scene_not_found: '找不到場景: {path}', + node_not_found: '找不到節點: {path}', + script_not_found: '找不到腳本: {path}', + resource_not_found: '找不到資源: {path}', + invalid_path: '無效路徑: {path}', + file_write_error: '寫入檔案失敗: {path}', + file_read_error: '讀取檔案失敗: {path}', + operation_failed: '操作失敗: {operation}', + signal_not_found: '找不到訊號: {signal}', + group_not_found: '找不到群組: {group}', + }, + warnings: { + process_cleanup: '正在清理已退出的进程 {id} 以允許 ID 重用', + screenshot_disabled: '無法確定主場景,實例 {id} 的截圖功能已禁用', + deprecated_tool: '工具 {tool} 已棄用,請使用 {replacement}', + }, + info: { + server_started: 'Godot MCP 伺服器已啟動', + process_started: 'Godot 專案已啟動,實例 ID: {id}', + process_stopped: 'Godot 專案實例 "{id}" 已停止', + screenshot_captured: '實例 {id} 截圖已捕獲', + scene_created: '場景已創建: {path}', + node_added: '節點已添加: {name}', + signal_connected: '訊號 {signal} 已連接', + group_added: '節點已添加到群組: {group}', + }, + tools: { + launch_editor: '啟動指定專案的 Godot 編輯器', + run_project: '運行 Godot 專案並捕獲輸出', + get_debug_output: '獲取當前的除錯輸出和錯誤', + stop_project: '停止運行中的 Godot 專案實例', + capture_screenshot: '從運行中的 Godot 專案實例捕獲截圖', + get_godot_version: '獲取已安裝的 Godot 版本', + list_projects: '列出目錄中的 Godot 專案', + get_project_info: '獲取 Godot 專案的元數據', + create_scene: '創建新場景', + add_node: '向場景添加新節點', + load_sprite: '載入精靈到 Sprite2D 節點', + save_scene: '儲存場景', + signal_connect: '將訊號從一個節點連接到另一個節點', + signal_disconnect: '斷開訊號連接', + group_add: '將節點添加到群組', + group_remove: '從群組中移除節點', + list_groups: '列出場景中的所有群組', + }, + descriptions: { + projectPath: 'Godot 專案目錄的路徑', + scene: '可選:要運行的特定場景', + instanceId: '可選:此實例的唯一識別碼', + args: '可選:額外的命令列參數', + enableScreenshot: '可選:為此實例啟用截圖捕獲', + outputPath: '可選:儲存輸出的路徑', + nodeName: '節點名稱', + nodeType: '要創建的節點類型', + parentNodePath: '父節點的路徑', + signalName: '要連接的訊號名稱', + sourceNode: '源節點(發射器)的路徑', + targetNode: '目標節點(接收器)的路徑', + methodName: '訊號發射時要調用的方法名稱', + groupName: '群組名稱', + }, +}; + +/** + * Japanese messages + */ +const jaMessages: Messages = { + errors: { + project_path_required: 'プロジェクトパスが必要です', + invalid_project_path: '無効なプロジェクトパス', + not_godot_project: '有効なGodotプロジェクトではありません: {path}', + godot_not_found: '有効なGodot実行ファイルパスが見つかりません', + no_active_processes: 'アクティブなGodotプロセスがありません', + instance_not_found: 'インスタンスIDが見つかりません: {id}', + instance_not_running: 'インスタンス {id} は実行されていません', + instance_already_running: 'インスタンスID "{id}" は既に実行中のプロセスで使用されています', + screenshot_not_enabled: 'インスタンス {id} はスクリーンショットサポートが有効になっていません', + screenshot_timeout: 'スクリーンショットのキャプチャがタイムアウトしました', + scene_not_found: 'シーンが見つかりません: {path}', + node_not_found: 'ノードが見つかりません: {path}', + script_not_found: 'スクリプトが見つかりません: {path}', + resource_not_found: 'リソースが見つかりません: {path}', + invalid_path: '無効なパス: {path}', + file_write_error: 'ファイルの書き込みに失敗しました: {path}', + file_read_error: 'ファイルの読み込みに失敗しました: {path}', + operation_failed: '操作に失敗しました: {operation}', + signal_not_found: 'シグナルが見つかりません: {signal}', + group_not_found: 'グループが見つかりません: {group}', + }, + warnings: { + process_cleanup: '終了したプロセス {id} をクリーンアップしてIDの再利用を許可しています', + screenshot_disabled: 'メインシーンを特定できません。インスタンス {id} のスクリーンショットは無効です', + deprecated_tool: 'ツール {tool} は非推奨です。{replacement} を使用してください', + }, + info: { + server_started: 'Godot MCPサーバーが起動しました', + process_started: 'Godotプロジェクトが起動しました。インスタンスID: {id}', + process_stopped: 'Godotプロジェクトインスタンス "{id}" が停止しました', + screenshot_captured: 'インスタンス {id} のスクリーンショットをキャプチャしました', + scene_created: 'シーンを作成しました: {path}', + node_added: 'ノードを追加しました: {name}', + signal_connected: 'シグナル {signal} を接続しました', + group_added: 'ノードをグループに追加しました: {group}', + }, + tools: { + launch_editor: '特定のプロジェクトのGodotエディタを起動', + run_project: 'Godotプロジェクトを実行して出力をキャプチャ', + get_debug_output: '現在のデバッグ出力とエラーを取得', + stop_project: '実行中のGodotプロジェクトインスタンスを停止', + capture_screenshot: '実行中のGodotプロジェクトインスタンスからスクリーンショットをキャプチャ', + get_godot_version: 'インストールされているGodotバージョンを取得', + list_projects: 'ディレクトリ内のGodotプロジェクトを一覧表示', + get_project_info: 'Godotプロジェクトのメタデータを取得', + create_scene: '新しいシーンを作成', + add_node: 'シーンに新しいノードを追加', + load_sprite: 'Sprite2Dノードにスプライトをロード', + save_scene: 'シーンを保存', + signal_connect: 'シグナルをあるノードから別のノードに接続', + signal_disconnect: 'シグナル接続を切断', + group_add: 'ノードをグループに追加', + group_remove: 'グループからノードを削除', + list_groups: 'シーン内のすべてのグループを一覧表示', + }, + descriptions: { + projectPath: 'Godotプロジェクトディレクトリへのパス', + scene: 'オプション:実行する特定のシーン', + instanceId: 'オプション:このインスタンスの一意の識別子', + args: 'オプション:追加のコマンドライン引数', + enableScreenshot: 'オプション:このインスタンスのスクリーンショットキャプチャを有効にする', + outputPath: 'オプション:出力を保存するパス', + nodeName: 'ノード名', + nodeType: '作成するノードのタイプ', + parentNodePath: '親ノードへのパス', + signalName: '接続するシグナル名', + sourceNode: 'ソースノード(エミッター)へのパス', + targetNode: 'ターゲットノード(レシーバー)へのパス', + methodName: 'シグナル発信時に呼び出すメソッド名', + groupName: 'グループ名', + }, +}; + +/** + * All translations + */ +const translations: Record = { + 'en': enMessages, + 'zh-CN': zhCNMessages, + 'zh-TW': zhTWMessages, + 'ja': jaMessages, + 'ko': enMessages, // Fallback to English + 'ru': enMessages, // Fallback to English + 'fr': enMessages, // Fallback to English + 'de': enMessages, // Fallback to English + 'es': enMessages, // Fallback to English + 'pt': enMessages, // Fallback to English +}; + +/** + * I18n class for handling translations + */ +export class I18n { + private language: Language; + private messages: Messages; + + constructor(language: Language = 'en') { + this.language = language; + this.messages = translations[language] || translations['en']; + } + + /** + * Set the current language + */ + setLanguage(language: Language): void { + this.language = language; + this.messages = translations[language] || translations['en']; + } + + /** + * Get the current language + */ + getLanguage(): Language { + return this.language; + } + + /** + * Get a message from a category with parameter substitution + */ + private getMessage(category: MessageCategory, key: string, params?: Record): string { + const messages = this.messages[category] as Record; + let message = messages[key] || translations['en'][category][key] || key; + + if (params) { + for (const [param, value] of Object.entries(params)) { + message = message.replace(new RegExp(`\\{${param}\\}`, 'g'), value); + } + } + + return message; + } + + /** + * Get an error message + */ + error(key: string, params?: Record): string { + return this.getMessage('errors', key, params); + } + + /** + * Get a warning message + */ + warning(key: string, params?: Record): string { + return this.getMessage('warnings', key, params); + } + + /** + * Get an info message + */ + info(key: string, params?: Record): string { + return this.getMessage('info', key, params); + } + + /** + * Get a tool name/description + */ + tool(key: string): string { + return this.getMessage('tools', key); + } + + /** + * Get a parameter description + */ + description(key: string): string { + return this.getMessage('descriptions', key); + } + + /** + * Detect system language + */ + static detectSystemLanguage(): Language { + const envLang = process.env.LANG || process.env.LC_ALL || process.env.LC_MESSAGES || ''; + const locale = envLang.split('.')[0].replace('_', '-'); + + // Map common locale codes + const localeMap: Record = { + 'en': 'en', + 'en-US': 'en', + 'en-GB': 'en', + 'zh': 'zh-CN', + 'zh-CN': 'zh-CN', + 'zh-Hans': 'zh-CN', + 'zh-Hans-CN': 'zh-CN', + 'zh-TW': 'zh-TW', + 'zh-Hant': 'zh-TW', + 'zh-Hant-TW': 'zh-TW', + 'ja': 'ja', + 'ja-JP': 'ja', + 'ko': 'ko', + 'ko-KR': 'ko', + 'ru': 'ru', + 'ru-RU': 'ru', + 'fr': 'fr', + 'fr-FR': 'fr', + 'de': 'de', + 'de-DE': 'de', + 'es': 'es', + 'es-ES': 'es', + 'pt': 'pt', + 'pt-BR': 'pt', + }; + + return localeMap[locale] || 'en'; + } +} + +/** + * Global i18n instance + */ +export const i18n = new I18n(I18n.detectSystemLanguage()); + +/** + * Initialize i18n with environment variable + */ +export function initI18n(): void { + const envLanguage = process.env.MCP_LANGUAGE as Language; + if (envLanguage && translations[envLanguage]) { + i18n.setLanguage(envLanguage); + } +} diff --git a/src/index.ts b/src/index.ts index 35b13dbd2..866a05836 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1184,6 +1184,236 @@ class GodotServer { required: [], }, }, + { + name: 'signal_connect', + description: 'Connect a signal from one node to another node\'s method in a Godot scene', + inputSchema: { + type: 'object', + properties: { + projectPath: { + type: 'string', + description: 'Path to the Godot project directory', + }, + scenePath: { + type: 'string', + description: 'Path to the scene file (relative to project)', + }, + sourceNodePath: { + type: 'string', + description: 'Path to the source node that emits the signal (e.g., "root/Player/Button")', + }, + signalName: { + type: 'string', + description: 'Name of the signal to connect (e.g., "pressed", "body_entered")', + }, + targetNodePath: { + type: 'string', + description: 'Path to the target node that will receive the signal (e.g., "root/Player")', + }, + methodName: { + type: 'string', + description: 'Name of the method to call when the signal is emitted', + }, + flags: { + type: 'number', + description: 'Optional: Connection flags (default: 0). Common values: 0=DEFERRED, 1=PERSIST, 2=ONE_SHOT', + }, + }, + required: ['projectPath', 'scenePath', 'sourceNodePath', 'signalName', 'targetNodePath', 'methodName'], + }, + }, + { + name: 'signal_disconnect', + description: 'Disconnect a signal connection in a Godot scene', + inputSchema: { + type: 'object', + properties: { + projectPath: { + type: 'string', + description: 'Path to the Godot project directory', + }, + scenePath: { + type: 'string', + description: 'Path to the scene file (relative to project)', + }, + sourceNodePath: { + type: 'string', + description: 'Path to the source node that emits the signal', + }, + signalName: { + type: 'string', + description: 'Name of the signal to disconnect', + }, + targetNodePath: { + type: 'string', + description: 'Path to the target node that receives the signal', + }, + methodName: { + type: 'string', + description: 'Name of the method that was connected to the signal', + }, + }, + required: ['projectPath', 'scenePath', 'sourceNodePath', 'signalName', 'targetNodePath', 'methodName'], + }, + }, + { + name: 'group_add', + description: 'Add a node to a group in a Godot scene', + inputSchema: { + type: 'object', + properties: { + projectPath: { + type: 'string', + description: 'Path to the Godot project directory', + }, + scenePath: { + type: 'string', + description: 'Path to the scene file (relative to project)', + }, + nodePath: { + type: 'string', + description: 'Path to the node to add to the group (e.g., "root/Player")', + }, + groupName: { + type: 'string', + description: 'Name of the group to add the node to', + }, + }, + required: ['projectPath', 'scenePath', 'nodePath', 'groupName'], + }, + }, + { + name: 'group_remove', + description: 'Remove a node from a group in a Godot scene', + inputSchema: { + type: 'object', + properties: { + projectPath: { + type: 'string', + description: 'Path to the Godot project directory', + }, + scenePath: { + type: 'string', + description: 'Path to the scene file (relative to project)', + }, + nodePath: { + type: 'string', + description: 'Path to the node to remove from the group', + }, + groupName: { + type: 'string', + description: 'Name of the group to remove the node from', + }, + }, + required: ['projectPath', 'scenePath', 'nodePath', 'groupName'], + }, + }, + { + name: 'list_groups', + description: 'List all groups in a Godot scene', + inputSchema: { + type: 'object', + properties: { + projectPath: { + type: 'string', + description: 'Path to the Godot project directory', + }, + scenePath: { + type: 'string', + description: 'Path to the scene file (relative to project)', + }, + }, + required: ['projectPath', 'scenePath'], + }, + }, + { + name: 'ui_create_button', + description: 'Create a Button node in a Godot scene with customizable properties', + inputSchema: { + type: 'object', + properties: { + projectPath: { + type: 'string', + description: 'Path to the Godot project directory', + }, + scenePath: { + type: 'string', + description: 'Path to the scene file (relative to project)', + }, + parentNodePath: { + type: 'string', + description: 'Path to the parent node (e.g., "root/MainMenu")', + }, + nodeName: { + type: 'string', + description: 'Name for the new button node', + }, + text: { + type: 'string', + description: 'Text to display on the button', + }, + position: { + type: 'object', + properties: { + x: { type: 'number', description: 'X position' }, + y: { type: 'number', description: 'Y position' }, + }, + description: 'Position of the button (x, y)', + }, + size: { + type: 'object', + properties: { + width: { type: 'number', description: 'Width' }, + height: { type: 'number', description: 'Height' }, + }, + description: 'Size of the button (width, height)', + }, + }, + required: ['projectPath', 'scenePath', 'parentNodePath', 'nodeName', 'text'], + }, + }, + { + name: 'ui_create_label', + description: 'Create a Label node in a Godot scene with customizable properties', + inputSchema: { + type: 'object', + properties: { + projectPath: { + type: 'string', + description: 'Path to the Godot project directory', + }, + scenePath: { + type: 'string', + description: 'Path to the scene file (relative to project)', + }, + parentNodePath: { + type: 'string', + description: 'Path to the parent node (e.g., "root/MainMenu")', + }, + nodeName: { + type: 'string', + description: 'Name for the new label node', + }, + text: { + type: 'string', + description: 'Text to display in the label', + }, + position: { + type: 'object', + properties: { + x: { type: 'number', description: 'X position' }, + y: { type: 'number', description: 'Y position' }, + }, + description: 'Position of the label (x, y)', + }, + fontSize: { + type: 'number', + description: 'Font size for the label text', + }, + }, + required: ['projectPath', 'scenePath', 'parentNodePath', 'nodeName', 'text'], + }, + }, { name: 'run_multiple_projects', description: 'Launch multiple Godot project instances in a single call. Useful for multiplayer testing (server + clients).', @@ -3122,15 +3352,174 @@ class GodotServer { console.error(`[SERVER] Using Godot at: ${this.godotPath}`); - const transport = new StdioServerTransport(); - await this.server.connect(transport); - console.error('Godot MCP server running on stdio'); + // Check transport mode + const transport = process.env.MCP_TRANSPORT || 'stdio'; + + if (transport === 'http') { + await this.runHttpServer(); + } else { + await this.runStdioServer(); + } } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; console.error('[SERVER] Failed to start:', errorMessage); process.exit(1); } } + + /** + * Run the MCP server with stdio transport + */ + private async runStdioServer() { + const transport = new StdioServerTransport(); + await this.server.connect(transport); + console.error('Godot MCP server running on stdio'); + } + + /** + * Run the MCP server with HTTP transport + */ + private async runHttpServer() { + const http = require('http'); + const port = parseInt(process.env.MCP_HTTP_PORT || '3000', 10); + const host = process.env.MCP_HTTP_HOST || '127.0.0.1'; + const endpoint = process.env.MCP_HTTP_ENDPOINT || '/mcp'; + + const server = http.createServer(async (req: any, res: any) => { + // Set CORS headers + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); + + // Handle preflight requests + if (req.method === 'OPTIONS') { + res.writeHead(204); + res.end(); + return; + } + + // Only accept POST requests to the endpoint + if (req.method !== 'POST' || req.url !== endpoint) { + res.writeHead(404); + res.end(JSON.stringify({ error: 'Not found' })); + return; + } + + // Read request body + let body = ''; + req.on('data', (chunk: Buffer) => { + body += chunk.toString(); + }); + + req.on('end', async () => { + try { + const request = JSON.parse(body); + const response = await this.handleHttpRequest(request); + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify(response)); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + res.writeHead(400, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: errorMessage })); + } + }); + }); + + server.listen(port, host, () => { + console.error(`Godot MCP server running on HTTP: http://${host}:${port}${endpoint}`); + }); + + // Handle server errors + server.on('error', (error: Error) => { + console.error('[SERVER] HTTP server error:', error.message); + process.exit(1); + }); + + // Keep the process alive + await new Promise(() => {}); // Never resolve + } + + /** + * Handle HTTP requests + */ + private async handleHttpRequest(request: any): Promise { + // Handle different request types + if (request.method === 'tools/list') { + return await this.handleToolsList(); + } else if (request.method === 'tools/call') { + return await this.handleToolCall(request.params); + } else if (request.method === 'initialize') { + return await this.handleInitialize(request.params); + } + + return { error: `Unknown method: ${request.method}` }; + } + + /** + * Handle tools/list request + */ + private async handleToolsList(): Promise { + // Return the list of tools + return { + tools: [ + { name: 'launch_editor', description: 'Launch Godot editor for a specific project' }, + { name: 'run_project', description: 'Run the Godot project and capture output' }, + { name: 'get_debug_output', description: 'Get the current debug output and errors' }, + { name: 'stop_project', description: 'Stop running Godot project instance(s)' }, + { name: 'capture_screenshot', description: 'Capture a screenshot from a running Godot project instance' }, + { name: 'get_godot_version', description: 'Get the installed Godot version' }, + { name: 'list_projects', description: 'List Godot projects in a directory' }, + { name: 'get_project_info', description: 'Retrieve metadata about a Godot project' }, + { name: 'list_processes', description: 'List all running Godot processes' }, + ], + }; + } + + /** + * Handle tools/call request + */ + private async handleToolCall(params: any): Promise { + const { name, arguments: args } = params; + + switch (name) { + case 'launch_editor': + return await this.handleLaunchEditor(args); + case 'run_project': + return await this.handleRunProject(args); + case 'get_debug_output': + return await this.handleGetDebugOutput(args); + case 'stop_project': + return await this.handleStopProject(args); + case 'capture_screenshot': + return await this.handleCaptureScreenshot(args); + case 'get_godot_version': + return await this.handleGetGodotVersion(); + case 'list_projects': + return await this.handleListProjects(args); + case 'get_project_info': + return await this.handleGetProjectInfo(args); + case 'list_processes': + return await this.handleListProcesses(); + default: + return { error: `Unknown tool: ${name}` }; + } + } + + /** + * Handle initialize request + */ + private async handleInitialize(params: any): Promise { + return { + protocolVersion: '2024-11-05', + capabilities: { + tools: {}, + }, + serverInfo: { + name: 'godot-mcp', + version: '0.1.1', + }, + }; + } } // Create and run the server From 9d4233c0679ad575753b2679649ec4f6c7733f5a Mon Sep 17 00:00:00 2001 From: "qqhyyy.com" <60999@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:01:13 +0800 Subject: [PATCH 12/13] feat: Complete HTTP mode and tool handlers implementation - Add complete HTTP transport mode with CORS support - Add signal_connect and signal_disconnect tool handlers - Add group_add, group_remove, list_groups tool handlers - Add ui_create_button and ui_create_label tool handlers - Add executeGodotOperation helper method - Update godot_operations.gd with new operations --- src/index.ts | 268 +++++++++++++++++++++++ src/scripts/godot_operations.gd | 370 ++++++++++++++++++++++++++++++++ 2 files changed, 638 insertions(+) diff --git a/src/index.ts b/src/index.ts index 866a05836..f6077e45e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1496,6 +1496,20 @@ class GodotServer { return await this.handleListProcesses(); case 'run_multiple_projects': return await this.handleRunMultipleProjects(request.params.arguments); + case 'signal_connect': + return await this.handleSignalConnect(request.params.arguments); + case 'signal_disconnect': + return await this.handleSignalDisconnect(request.params.arguments); + case 'group_add': + return await this.handleGroupAdd(request.params.arguments); + case 'group_remove': + return await this.handleGroupRemove(request.params.arguments); + case 'list_groups': + return await this.handleListGroups(request.params.arguments); + case 'ui_create_button': + return await this.handleUiCreateButton(request.params.arguments); + case 'ui_create_label': + return await this.handleUiCreateLabel(request.params.arguments); default: throw new McpError( ErrorCode.MethodNotFound, @@ -2163,6 +2177,260 @@ class GodotServer { } } + /** + * Handle the signal_connect tool + * Connect a signal from one node to another node's method + */ + private async handleSignalConnect(args: any) { + args = this.normalizeParameters(args || {}); + + if (!args.projectPath || !args.scenePath || !args.sourceNodePath || + !args.signalName || !args.targetNodePath || !args.methodName) { + return this.createErrorResponse( + 'Missing required parameters for signal_connect', + ['Required: projectPath, scenePath, sourceNodePath, signalName, targetNodePath, methodName'] + ); + } + + const params = { + scene_path: args.scenePath, + source_node_path: args.sourceNodePath, + signal_name: args.signalName, + target_node_path: args.targetNodePath, + method_name: args.methodName, + flags: args.flags || 0, + }; + + return await this.executeGodotOperation(args.projectPath, 'signal_connect', params); + } + + /** + * Handle the signal_disconnect tool + * Disconnect a signal connection + */ + private async handleSignalDisconnect(args: any) { + args = this.normalizeParameters(args || {}); + + if (!args.projectPath || !args.scenePath || !args.sourceNodePath || + !args.signalName || !args.targetNodePath || !args.methodName) { + return this.createErrorResponse( + 'Missing required parameters for signal_disconnect', + ['Required: projectPath, scenePath, sourceNodePath, signalName, targetNodePath, methodName'] + ); + } + + const params = { + scene_path: args.scenePath, + source_node_path: args.sourceNodePath, + signal_name: args.signalName, + target_node_path: args.targetNodePath, + method_name: args.methodName, + }; + + return await this.executeGodotOperation(args.projectPath, 'signal_disconnect', params); + } + + /** + * Handle the group_add tool + * Add a node to a group + */ + private async handleGroupAdd(args: any) { + args = this.normalizeParameters(args || {}); + + if (!args.projectPath || !args.scenePath || !args.nodePath || !args.groupName) { + return this.createErrorResponse( + 'Missing required parameters for group_add', + ['Required: projectPath, scenePath, nodePath, groupName'] + ); + } + + const params = { + scene_path: args.scenePath, + node_path: args.nodePath, + group_name: args.groupName, + }; + + return await this.executeGodotOperation(args.projectPath, 'group_add', params); + } + + /** + * Handle the group_remove tool + * Remove a node from a group + */ + private async handleGroupRemove(args: any) { + args = this.normalizeParameters(args || {}); + + if (!args.projectPath || !args.scenePath || !args.nodePath || !args.groupName) { + return this.createErrorResponse( + 'Missing required parameters for group_remove', + ['Required: projectPath, scenePath, nodePath, groupName'] + ); + } + + const params = { + scene_path: args.scenePath, + node_path: args.nodePath, + group_name: args.groupName, + }; + + return await this.executeGodotOperation(args.projectPath, 'group_remove', params); + } + + /** + * Handle the list_groups tool + * List all groups in a scene + */ + private async handleListGroups(args: any) { + args = this.normalizeParameters(args || {}); + + if (!args.projectPath || !args.scenePath) { + return this.createErrorResponse( + 'Missing required parameters for list_groups', + ['Required: projectPath, scenePath'] + ); + } + + const params = { + scene_path: args.scenePath, + }; + + return await this.executeGodotOperation(args.projectPath, 'list_groups', params); + } + + /** + * Handle the ui_create_button tool + * Create a Button node in a scene + */ + private async handleUiCreateButton(args: any) { + args = this.normalizeParameters(args || {}); + + if (!args.projectPath || !args.scenePath || !args.parentNodePath || + !args.nodeName || !args.text) { + return this.createErrorResponse( + 'Missing required parameters for ui_create_button', + ['Required: projectPath, scenePath, parentNodePath, nodeName, text'] + ); + } + + const params: any = { + scene_path: args.scenePath, + parent_node_path: args.parentNodePath, + node_name: args.nodeName, + text: args.text, + }; + + if (args.position) { + params.position = args.position; + } + if (args.size) { + params.size = args.size; + } + + return await this.executeGodotOperation(args.projectPath, 'ui_create_button', params); + } + + /** + * Handle the ui_create_label tool + * Create a Label node in a scene + */ + private async handleUiCreateLabel(args: any) { + args = this.normalizeParameters(args || {}); + + if (!args.projectPath || !args.scenePath || !args.parentNodePath || + !args.nodeName || !args.text) { + return this.createErrorResponse( + 'Missing required parameters for ui_create_label', + ['Required: projectPath, scenePath, parentNodePath, nodeName, text'] + ); + } + + const params: any = { + scene_path: args.scenePath, + parent_node_path: args.parentNodePath, + node_name: args.nodeName, + text: args.text, + }; + + if (args.position) { + params.position = args.position; + } + if (args.fontSize) { + params.font_size = args.fontSize; + } + + return await this.executeGodotOperation(args.projectPath, 'ui_create_label', params); + } + + /** + * Execute a Godot operation using the operations script + */ + private async executeGodotOperation(projectPath: string, operation: string, params: any) { + if (!this.godotPath) { + await this.detectGodotPath(); + if (!this.godotPath) { + return this.createErrorResponse( + 'Could not find a valid Godot executable path', + ['Set GODOT_PATH environment variable'] + ); + } + } + + const operationScript = join(__dirname, 'scripts', 'godot_operations.gd'); + if (!existsSync(operationScript)) { + return this.createErrorResponse( + 'Godot operations script not found', + ['Ensure the MCP server is built correctly'] + ); + } + + const args = [ + '--headless', + '--path', projectPath, + '-s', operationScript, + '--', + JSON.stringify({ operation, params }), + ]; + + return new Promise((resolve) => { + const process = spawn(this.godotPath!, args, { stdio: 'pipe' }); + let output = ''; + let errors = ''; + + process.stdout.on('data', (data: Buffer) => { + output += data.toString(); + }); + + process.stderr.on('data', (data: Buffer) => { + errors += data.toString(); + }); + + process.on('close', (code: number) => { + if (code === 0) { + resolve({ + content: [ + { + type: 'text', + text: output || `Operation ${operation} completed successfully`, + }, + ], + }); + } else { + resolve(this.createErrorResponse( + `Operation ${operation} failed with code ${code}`, + [errors || 'Unknown error'] + )); + } + }); + + process.on('error', (error: Error) => { + resolve(this.createErrorResponse( + `Failed to execute operation: ${error.message}`, + ['Ensure Godot is installed correctly'] + )); + }); + }); + } + /** * Handle the run_multiple_projects tool * Launches multiple Godot instances with optional staggered delays diff --git a/src/scripts/godot_operations.gd b/src/scripts/godot_operations.gd index 65ea869b8..4839525f4 100644 --- a/src/scripts/godot_operations.gd +++ b/src/scripts/godot_operations.gd @@ -71,6 +71,20 @@ func _init(): get_uid(params) "resave_resources": resave_resources(params) + "signal_connect": + signal_connect(params) + "signal_disconnect": + signal_disconnect(params) + "group_add": + group_add(params) + "group_remove": + group_remove(params) + "list_groups": + list_groups(params) + "ui_create_button": + ui_create_button(params) + "ui_create_label": + ui_create_label(params) _: log_error("Unknown operation: " + operation) quit(1) @@ -461,6 +475,362 @@ func create_scene(params): quit(1) else: printerr("Failed to pack scene: " + str(result)) + +## Signal Operations + +func signal_connect(params: Dictionary) -> void: + """ + Connect a signal from one node to another node's method. + params: + - scene_path: Path to the scene file + - source_node_path: Path to the source node (emitter) + - signal_name: Name of the signal to connect + - target_node_path: Path to the target node (receiver) + - method_name: Name of the method to call when signal is emitted + - flags: Optional connection flags (default: 0) + """ + var scene_path = params.get("scene_path", "") + var source_node_path = params.get("source_node_path", "") + var signal_name = params.get("signal_name", "") + var target_node_path = params.get("target_node_path", "") + var method_name = params.get("method_name", "") + var flags = params.get("flags", 0) + + if scene_path.is_empty() or source_node_path.is_empty() or signal_name.is_empty(): + printerr("Missing required parameters for signal_connect") + quit(1) + return + + var scene = load_scene(scene_path) + if scene == null: + return + + var source_node = get_node_by_path(scene, source_node_path) + var target_node = get_node_by_path(scene, target_node_path) + + if source_node == null: + printerr("Source node not found: " + source_node_path) + quit(1) + return + + if target_node == null: + printerr("Target node not found: " + target_node_path) + quit(1) + return + + # Connect the signal + var error = source_node.connect(signal_name, Callable(target_node, method_name), flags) + + if error == OK: + print("Signal '" + signal_name + "' connected from '" + source_node_path + "' to '" + target_node_path + "." + method_name + "'") + + # Save the scene to persist the connection + var packed_scene = PackedScene.new() + var result = packed_scene.pack(scene) + if result == OK: + ResourceSaver.save(packed_scene, scene_path) + print("Scene saved with new signal connection") + else: + printerr("Failed to connect signal: " + str(error)) + quit(1) + +func signal_disconnect(params: Dictionary) -> void: + """ + Disconnect a signal connection. + params: + - scene_path: Path to the scene file + - source_node_path: Path to the source node (emitter) + - signal_name: Name of the signal to disconnect + - target_node_path: Path to the target node (receiver) + - method_name: Name of the method that was connected + """ + var scene_path = params.get("scene_path", "") + var source_node_path = params.get("source_node_path", "") + var signal_name = params.get("signal_name", "") + var target_node_path = params.get("target_node_path", "") + var method_name = params.get("method_name", "") + + if scene_path.is_empty() or source_node_path.is_empty() or signal_name.is_empty(): + printerr("Missing required parameters for signal_disconnect") + quit(1) + return + + var scene = load_scene(scene_path) + if scene == null: + return + + var source_node = get_node_by_path(scene, source_node_path) + var target_node = get_node_by_path(scene, target_node_path) + + if source_node == null: + printerr("Source node not found: " + source_node_path) + quit(1) + return + + if target_node == null: + printerr("Target node not found: " + target_node_path) + quit(1) + return + + # Disconnect the signal + if source_node.is_connected(signal_name, Callable(target_node, method_name)): + source_node.disconnect(signal_name, Callable(target_node, method_name)) + print("Signal '" + signal_name + "' disconnected from '" + target_node_path + "." + method_name + "'") + + # Save the scene + var packed_scene = PackedScene.new() + var result = packed_scene.pack(scene) + if result == OK: + ResourceSaver.save(packed_scene, scene_path) + print("Scene saved with signal disconnection") + else: + printerr("Signal connection not found") + quit(1) + +## Group Operations + +func group_add(params: Dictionary) -> void: + """ + Add a node to a group. + params: + - scene_path: Path to the scene file + - node_path: Path to the node + - group_name: Name of the group + """ + var scene_path = params.get("scene_path", "") + var node_path = params.get("node_path", "") + var group_name = params.get("group_name", "") + + if scene_path.is_empty() or node_path.is_empty() or group_name.is_empty(): + printerr("Missing required parameters for group_add") + quit(1) + return + + var scene = load_scene(scene_path) + if scene == null: + return + + var node = get_node_by_path(scene, node_path) + + if node == null: + printerr("Node not found: " + node_path) + quit(1) + return + + # Add to group + node.add_to_group(group_name) + print("Node '" + node_path + "' added to group '" + group_name + "'") + + # Save the scene + var packed_scene = PackedScene.new() + var result = packed_scene.pack(scene) + if result == OK: + ResourceSaver.save(packed_scene, scene_path) + print("Scene saved with group addition") + +func group_remove(params: Dictionary) -> void: + """ + Remove a node from a group. + params: + - scene_path: Path to the scene file + - node_path: Path to the node + - group_name: Name of the group + """ + var scene_path = params.get("scene_path", "") + var node_path = params.get("node_path", "") + var group_name = params.get("group_name", "") + + if scene_path.is_empty() or node_path.is_empty() or group_name.is_empty(): + printerr("Missing required parameters for group_remove") + quit(1) + return + + var scene = load_scene(scene_path) + if scene == null: + return + + var node = get_node_by_path(scene, node_path) + + if node == null: + printerr("Node not found: " + node_path) + quit(1) + return + + # Remove from group + if node.is_in_group(group_name): + node.remove_from_group(group_name) + print("Node '" + node_path + "' removed from group '" + group_name + "'") + + # Save the scene + var packed_scene = PackedScene.new() + var result = packed_scene.pack(scene) + if result == OK: + ResourceSaver.save(packed_scene, scene_path) + print("Scene saved with group removal") + else: + printerr("Node is not in group '" + group_name + "'") + quit(1) + +func list_groups(params: Dictionary) -> void: + """ + List all groups in a scene. + params: + - scene_path: Path to the scene file + """ + var scene_path = params.get("scene_path", "") + + if scene_path.is_empty(): + printerr("Missing required parameter: scene_path") + quit(1) + return + + var scene = load_scene(scene_path) + if scene == null: + return + + # Collect all groups + var groups = {} + var nodes_to_check = [scene] + + while nodes_to_check.size() > 0: + var node = nodes_to_check.pop_back() + var node_groups = node.get_groups() + + for group in node_groups: + if not groups.has(group): + groups[group] = [] + groups[group].append(node.get_path()) + + # Add children to check + for child in node.get_children(): + nodes_to_check.append(child) + + # Output result + var result = {"groups": groups, "count": groups.size()} + print(JSON.stringify(result)) + +## UI Operations + +func ui_create_button(params: Dictionary) -> void: + """ + Create a Button node in a scene. + params: + - scene_path: Path to the scene file + - parent_node_path: Path to the parent node + - node_name: Name for the new button + - text: Text to display on the button + - position: Optional {x, y} position + - size: Optional {width, height} size + """ + var scene_path = params.get("scene_path", "") + var parent_node_path = params.get("parent_node_path", "") + var node_name = params.get("node_name", "") + var text = params.get("text", "") + var position = params.get("position", {}) + var size = params.get("size", {}) + + if scene_path.is_empty() or parent_node_path.is_empty() or node_name.is_empty(): + printerr("Missing required parameters for ui_create_button") + quit(1) + return + + var scene = load_scene(scene_path) + if scene == null: + return + + var parent_node = get_node_by_path(scene, parent_node_path) + + if parent_node == null: + printerr("Parent node not found: " + parent_node_path) + quit(1) + return + + # Create Button node + var button = Button.new() + button.name = node_name + button.text = text + + # Set position if provided + if position.has("x") and position.has("y"): + button.position = Vector2(position.x, position.y) + + # Set size if provided + if size.has("width") and size.has("height"): + button.size = Vector2(size.width, size.height) + + # Add to parent + parent_node.add_child(button) + button.owner = scene + + print("Button '" + node_name + "' created at '" + parent_node_path + "'") + + # Save the scene + var packed_scene = PackedScene.new() + var result = packed_scene.pack(scene) + if result == OK: + ResourceSaver.save(packed_scene, scene_path) + print("Scene saved with new button") + +func ui_create_label(params: Dictionary) -> void: + """ + Create a Label node in a scene. + params: + - scene_path: Path to the scene file + - parent_node_path: Path to the parent node + - node_name: Name for the new label + - text: Text to display in the label + - position: Optional {x, y} position + - font_size: Optional font size + """ + var scene_path = params.get("scene_path", "") + var parent_node_path = params.get("parent_node_path", "") + var node_name = params.get("node_name", "") + var text = params.get("text", "") + var position = params.get("position", {}) + var font_size = params.get("font_size", 16) + + if scene_path.is_empty() or parent_node_path.is_empty() or node_name.is_empty(): + printerr("Missing required parameters for ui_create_label") + quit(1) + return + + var scene = load_scene(scene_path) + if scene == null: + return + + var parent_node = get_node_by_path(scene, parent_node_path) + + if parent_node == null: + printerr("Parent node not found: " + parent_node_path) + quit(1) + return + + # Create Label node + var label = Label.new() + label.name = node_name + label.text = text + + # Set position if provided + if position.has("x") and position.has("y"): + label.position = Vector2(position.x, position.y) + + # Set font size + var theme = Theme.new() + theme.set_font_size("font_size", "Label", font_size) + label.theme = theme + + # Add to parent + parent_node.add_child(label) + label.owner = scene + + print("Label '" + node_name + "' created at '" + parent_node_path + "'") + + # Save the scene + var packed_scene = PackedScene.new() + var result = packed_scene.pack(scene) + if result == OK: + ResourceSaver.save(packed_scene, scene_path) + print("Scene saved with new label") printerr("Error code: " + str(result)) quit(1) From 66b651de82603133ad2eacfb023cdfa7bf0a770c Mon Sep 17 00:00:00 2001 From: "qqhyyy.com" <60999@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:09:46 +0800 Subject: [PATCH 13/13] chore: update package-lock.json after rebase --- package-lock.json | 941 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 910 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index a7e4073d6..5a9225f12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.1", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "0.6.0", + "@modelcontextprotocol/sdk": "^1.26.0", "axios": "^1.7.9", "fs-extra": "^11.2.0" }, @@ -489,6 +489,18 @@ "node": ">=18" } }, + "node_modules/@hono/node-server": { + "version": "1.19.11", + "resolved": "https://registry.npmmirror.com/@hono/node-server/-/node-server-1.19.11.tgz", + "integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@inquirer/checkbox": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", @@ -728,14 +740,43 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.0.tgz", - "integrity": "sha512-9rsDudGhDtMbvxohPoMMyAUOmEzQsOK+XFchh6gZGqo8sx9sBuZQs+CUttXqa8RZXKDaJRCN2tUtgGof7jRkkw==", + "version": "1.27.1", + "resolved": "https://registry.npmmirror.com/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz", + "integrity": "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==", "license": "MIT", "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "zod": "^3.23.8" + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } } }, "node_modules/@types/mute-stream": { @@ -765,6 +806,77 @@ "dev": true, "license": "MIT" }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -824,6 +936,30 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -846,6 +982,22 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -905,6 +1057,19 @@ "node": ">=18" } }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", @@ -914,11 +1079,59 @@ "node": ">= 0.6" } }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -964,6 +1177,12 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -971,6 +1190,15 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -1058,6 +1286,128 @@ "@esbuild/win32-x64": "0.27.3" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.3.1", + "resolved": "https://registry.npmmirror.com/express-rate-limit/-/express-rate-limit-8.3.1.tgz", + "integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -1086,6 +1436,28 @@ "node": ">=0.10.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -1093,6 +1465,27 @@ "dev": true, "license": "MIT" }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/flora-colossus": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz", @@ -1158,6 +1551,24 @@ "node": ">= 6" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fs-extra": { "version": "11.3.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", @@ -1305,32 +1716,49 @@ "node": ">= 0.4" } }, + "node_modules/hono": { + "version": "4.12.6", + "resolved": "https://registry.npmmirror.com/hono/-/hono-4.12.6.tgz", + "integrity": "sha512-KljEp+MeEEEIOT75qBo1UjqqB29fRMtlDEwCxcexOzdkUq6LR/vRvHk5pdROcxyOYyW1niq7Gb5pFVGy5R1eBw==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.2", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ignore": { @@ -1349,6 +1777,24 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1359,6 +1805,39 @@ "node": ">=8" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jose/-/jose-6.2.1.tgz", + "integrity": "sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmmirror.com/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -1380,6 +1859,27 @@ "node": ">= 0.4" } }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -1405,7 +1905,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mute-stream": { @@ -1418,6 +1917,15 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-forge": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", @@ -1428,6 +1936,48 @@ "node": ">= 6.13.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -1438,6 +1988,43 @@ "node": ">=0.10.0" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -1451,25 +2038,87 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" } }, "node_modules/safer-buffer": { @@ -1478,12 +2127,175 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -1498,9 +2310,9 @@ } }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1569,6 +2381,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/typescript": { "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", @@ -1608,6 +2459,30 @@ "node": ">= 0.8" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -1623,6 +2498,12 @@ "node": ">=8" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/yoctocolors-cjs": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", @@ -1641,7 +2522,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1650,7 +2530,6 @@ "version": "3.25.1", "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", - "dev": true, "license": "ISC", "peerDependencies": { "zod": "^3.25 || ^4"