From 985e532c61e52fe842c20498c4b719763760efc3 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Sun, 21 Jun 2026 12:26:44 +0200 Subject: [PATCH] feat(vikunja-mcp): add task attachments and extra metadata parameters support --- packages/vikunja-mcp/package.json | 6 +- packages/vikunja-mcp/src/index.test.ts | 25 ++++++ packages/vikunja-mcp/src/index.ts | 118 +++++++++++++++++++++++-- pnpm-lock.yaml | 72 +++++++++++++-- 4 files changed, 205 insertions(+), 16 deletions(-) diff --git a/packages/vikunja-mcp/package.json b/packages/vikunja-mcp/package.json index ec856f0..92eb328 100644 --- a/packages/vikunja-mcp/package.json +++ b/packages/vikunja-mcp/package.json @@ -14,10 +14,14 @@ "@modelcontextprotocol/sdk": "^1.5.0", "axios": "^1.7.2", "dotenv": "^17.3.1", - "express": "^4.19.2" + "express": "^4.19.2", + "form-data": "^4.0.5", + "marked": "^18.0.5" }, "devDependencies": { "@types/express": "^4.17.21", + "@types/form-data": "^2.5.2", + "@types/marked": "^6.0.0", "@types/node": "^20.14.10", "tsx": "^4.19.2", "typescript": "^5.5.3", diff --git a/packages/vikunja-mcp/src/index.test.ts b/packages/vikunja-mcp/src/index.test.ts index 1e51b27..32c36d4 100644 --- a/packages/vikunja-mcp/src/index.test.ts +++ b/packages/vikunja-mcp/src/index.test.ts @@ -177,4 +177,29 @@ describe('Vikunja MCP Server - New Tools (RED)', () => { { id: 1, comment: 'New Comment', task_id: 42 } ]); }); + + it('should support vikunja_create_task and convert markdown description to HTML', async () => { + mockPut.mockResolvedValueOnce({ + data: { id: 11, title: 'Markdown Task', description: '

Bold and italic

\n' } + }); + + const handler = (server as any)._requestHandlers.get('tools/call'); + const response = await handler({ + method: 'tools/call', + params: { + name: 'vikunja_create_task', + arguments: { + project_id: 1, + title: 'Markdown Task', + description: '**Bold** and *italic*' + }, + }, + }); + + expect(mockPut).toHaveBeenCalledWith('/projects/1/tasks', expect.objectContaining({ + title: 'Markdown Task', + description: '

Bold and italic

\n' + })); + expect(response.isError).toBeUndefined(); + }); }); diff --git a/packages/vikunja-mcp/src/index.ts b/packages/vikunja-mcp/src/index.ts index 4ce2e97..e350c3e 100644 --- a/packages/vikunja-mcp/src/index.ts +++ b/packages/vikunja-mcp/src/index.ts @@ -8,6 +8,9 @@ import { } from "@modelcontextprotocol/sdk/types.js"; import axios from "axios"; import https from "https"; +import fs from "fs"; +import path from "path"; +import { marked } from "marked"; const VIKUNJA_BASE_URL = process.env.VIKUNJA_BASE_URL || "https://tasks.infra.mintel.me"; const VIKUNJA_API_TOKEN = process.env.VIKUNJA_API_TOKEN; @@ -63,6 +66,14 @@ const CREATE_TASK_TOOL: Tool = { project_id: { type: "number", description: "The ID of the project to add the task to" }, title: { type: "string", description: "The title of the task" }, description: { type: "string", description: "Optional description of the task" }, + parent_task_id: { type: "number", description: "Optional ID of a parent task to create a subtask" }, + due_date: { type: "string", description: "ISO 8601 Date string for due date" }, + start_date: { type: "string", description: "ISO 8601 Date string for start date" }, + end_date: { type: "string", description: "ISO 8601 Date string for end date" }, + priority: { type: "number", description: "Priority level (e.g. 1-5)" }, + percent_done: { type: "number", description: "Progress percentage" }, + hex_color: { type: "string", description: "Hex color code" }, + is_favorite: { type: "boolean", description: "Mark as favorite" } }, required: ["project_id", "title"], }, @@ -77,7 +88,14 @@ const UPDATE_TASK_TOOL: Tool = { task_id: { type: "number", description: "The ID of the task to update" }, done: { type: "boolean", description: "Set to true to mark the task as done" }, title: { type: "string", description: "Update the title" }, - description: { type: "string", description: "Update the description" } + description: { type: "string", description: "Update the description" }, + due_date: { type: "string", description: "ISO 8601 Date string for due date" }, + start_date: { type: "string", description: "ISO 8601 Date string for start date" }, + end_date: { type: "string", description: "ISO 8601 Date string for end date" }, + priority: { type: "number", description: "Priority level (e.g. 1-5)" }, + percent_done: { type: "number", description: "Progress percentage (0-1 or 0-100 depending on API version, try decimals like 0.5 first)" }, + hex_color: { type: "string", description: "Hex color code" }, + is_favorite: { type: "boolean", description: "Mark as favorite" } }, required: ["task_id"], }, @@ -146,6 +164,33 @@ const GET_TASK_COMMENTS_TOOL: Tool = { } }; +const GET_TASK_ATTACHMENT_TOOL: Tool = { + name: "vikunja_get_task_attachment", + description: "Download a task attachment to a local file", + inputSchema: { + type: "object", + properties: { + task_id: { type: "number", description: "The ID of the task" }, + attachment_id: { type: "number", description: "The ID of the attachment" }, + save_path: { type: "string", description: "Absolute path to save the file to" } + }, + required: ["task_id", "attachment_id", "save_path"] + } +}; + +const CREATE_TASK_ATTACHMENT_TOOL: Tool = { + name: "vikunja_upload_task_attachment", + description: "Upload an attachment file to a specific task", + inputSchema: { + type: "object", + properties: { + task_id: { type: "number", description: "The ID of the task" }, + file_path: { type: "string", description: "Absolute path to the local file to upload" } + }, + required: ["task_id", "file_path"] + } +}; + // --- Server Setup --- export const server = new Server( { name: "vikunja-mcp", version: "1.0.0" }, @@ -162,7 +207,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({ GET_TASK_TOOL, DELETE_TASK_TOOL, CREATE_TASK_COMMENT_TOOL, - GET_TASK_COMMENTS_TOOL + GET_TASK_COMMENTS_TOOL, + GET_TASK_ATTACHMENT_TOOL, + CREATE_TASK_ATTACHMENT_TOOL ], })); @@ -182,20 +229,47 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { } if (request.params.name === "vikunja_create_task") { - const { project_id, title, description } = request.params.arguments as any; - const res = await api.put(`/projects/${project_id}/tasks`, { + const { + project_id, title, description, parent_task_id, + due_date, start_date, end_date, priority, percent_done, hex_color, is_favorite + } = request.params.arguments as any; + + const payload: any = { title, - description: description || "" - }); + description: description ? await marked.parse(description) : "" + }; + + if (parent_task_id !== undefined) payload.parent_task_id = parent_task_id; + if (due_date !== undefined) payload.due_date = due_date; + if (start_date !== undefined) payload.start_date = start_date; + if (end_date !== undefined) payload.end_date = end_date; + if (priority !== undefined) payload.priority = priority; + if (percent_done !== undefined) payload.percent_done = percent_done; + if (hex_color !== undefined) payload.hex_color = hex_color; + if (is_favorite !== undefined) payload.is_favorite = is_favorite; + + const res = await api.put(`/projects/${project_id}/tasks`, payload); return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] }; } if (request.params.name === "vikunja_update_task") { - const { task_id, done, title, description } = request.params.arguments as any; + const { + task_id, done, title, description, + due_date, start_date, end_date, + priority, percent_done, hex_color, is_favorite + } = request.params.arguments as any; + const payload: any = {}; if (done !== undefined) payload.done = done; if (title !== undefined) payload.title = title; - if (description !== undefined) payload.description = description; + if (description !== undefined) payload.description = await marked.parse(description); + if (due_date !== undefined) payload.due_date = due_date; + if (start_date !== undefined) payload.start_date = start_date; + if (end_date !== undefined) payload.end_date = end_date; + if (priority !== undefined) payload.priority = priority; + if (percent_done !== undefined) payload.percent_done = percent_done; + if (hex_color !== undefined) payload.hex_color = hex_color; + if (is_favorite !== undefined) payload.is_favorite = is_favorite; const res = await api.post(`/tasks/${task_id}`, payload); return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] }; @@ -237,6 +311,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] }; } + if (request.params.name === "vikunja_get_task_attachment") { + const { task_id, attachment_id, save_path } = request.params.arguments as any; + const res = await api.get(`/tasks/${task_id}/attachments/${attachment_id}`, { + responseType: 'arraybuffer' + }); + fs.writeFileSync(save_path, Buffer.from(res.data)); + return { content: [{ type: "text", text: `Successfully saved attachment to ${save_path}` }] }; + } + + if (request.params.name === "vikunja_upload_task_attachment") { + const { task_id, file_path } = request.params.arguments as any; + if (!fs.existsSync(file_path)) { + throw new Error(`File not found: ${file_path}`); + } + + const buffer = fs.readFileSync(file_path); + const blob = new Blob([buffer]); + const formData = new FormData(); + formData.append('files', blob, path.basename(file_path)); + + const res = await api.put(`/tasks/${task_id}/attachments`, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }); + return { content: [{ type: "text", text: `Successfully uploaded attachment to task ${task_id}.\nResponse: ${JSON.stringify(res.data)}` }] }; + } + throw new Error(`Unknown tool: ${request.params.name}`); } catch (e: any) { const msg = e.response?.data?.message || e.message; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4b378f..119a993 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -235,7 +235,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.33)(@vitest/ui@4.0.18)(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.33)(@vitest/ui@4.0.18(vitest@4.0.18))(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) packages/content-engine: dependencies: @@ -537,7 +537,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.4 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.10)(@vitest/ui@4.0.18)(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.10)(@vitest/ui@4.0.18(vitest@4.0.18))(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) packages/meme-generator: dependencies: @@ -752,7 +752,7 @@ importers: version: 5.9.3 vitest: specifier: ^2.0.0 - version: 2.1.9(@types/node@22.19.10)(@vitest/ui@4.0.18)(happy-dom@20.5.3)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0) + version: 2.1.9(@types/node@22.19.10)(@vitest/ui@4.0.18(vitest@4.0.18))(happy-dom@20.5.3)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0) packages/outline-mcp: dependencies: @@ -928,7 +928,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.33)(@vitest/ui@4.0.18)(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.33)(@vitest/ui@4.0.18(vitest@4.0.18))(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) packages/serpbear-mcp: dependencies: @@ -1024,10 +1024,22 @@ importers: express: specifier: ^4.19.2 version: 4.22.1 + form-data: + specifier: ^4.0.5 + version: 4.0.5 + marked: + specifier: ^18.0.5 + version: 18.0.5 devDependencies: '@types/express': specifier: ^4.17.21 version: 4.17.25 + '@types/form-data': + specifier: ^2.5.2 + version: 2.5.2 + '@types/marked': + specifier: ^6.0.0 + version: 6.0.0 '@types/node': specifier: ^20.14.10 version: 20.19.33 @@ -2816,95 +2828,111 @@ packages: '@react-email/body@0.0.11': resolution: {integrity: sha512-ZSD2SxVSgUjHGrB0Wi+4tu3MEpB4fYSbezsFNEJk2xCWDBkFiOeEsjTmR5dvi+CxTK691hQTQlHv0XWuP7ENTg==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/button@0.0.19': resolution: {integrity: sha512-HYHrhyVGt7rdM/ls6FuuD6XE7fa7bjZTJqB2byn6/oGsfiEZaogY77OtoLL/mrQHjHjZiJadtAMSik9XLcm7+A==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/code-block@0.0.11': resolution: {integrity: sha512-4D43p+LIMjDzm66gTDrZch0Flkip5je91mAT7iGs6+SbPyalHgIA+lFQoQwhz/VzHHLxuD0LV6gwmU/WUQ2WEg==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/code-inline@0.0.5': resolution: {integrity: sha512-MmAsOzdJpzsnY2cZoPHFPk6uDO/Ncpb4Kh1hAt9UZc1xOW3fIzpe1Pi9y9p6wwUmpaeeDalJxAxH6/fnTquinA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/column@0.0.13': resolution: {integrity: sha512-Lqq17l7ShzJG/d3b1w/+lVO+gp2FM05ZUo/nW0rjxB8xBICXOVv6PqjDnn3FXKssvhO5qAV20lHM6S+spRhEwQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/components@0.0.33': resolution: {integrity: sha512-/GKdT3YijT1iEWPAXF644jr12w5xVgzUr0zlbZGt2KOkGeFHNZUCL5UtRopmnjrH/Fayf8Gjv6q/4E2cZgDtdQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/container@0.0.15': resolution: {integrity: sha512-Qo2IQo0ru2kZq47REmHW3iXjAQaKu4tpeq/M8m1zHIVwKduL2vYOBQWbC2oDnMtWPmkBjej6XxgtZByxM6cCFg==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/font@0.0.9': resolution: {integrity: sha512-4zjq23oT9APXkerqeslPH3OZWuh5X4crHK6nx82mVHV2SrLba8+8dPEnWbaACWTNjOCbcLIzaC9unk7Wq2MIXw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/head@0.0.12': resolution: {integrity: sha512-X2Ii6dDFMF+D4niNwMAHbTkeCjlYYnMsd7edXOsi0JByxt9wNyZ9EnhFiBoQdqkE+SMDcu8TlNNttMrf5sJeMA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/heading@0.0.15': resolution: {integrity: sha512-xF2GqsvBrp/HbRHWEfOgSfRFX+Q8I5KBEIG5+Lv3Vb2R/NYr0s8A5JhHHGf2pWBMJdbP4B2WHgj/VUrhy8dkIg==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/hr@0.0.11': resolution: {integrity: sha512-S1gZHVhwOsd1Iad5IFhpfICwNPMGPJidG/Uysy1AwmspyoAP5a4Iw3OWEpINFdgh9MHladbxcLKO2AJO+cA9Lw==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/html@0.0.11': resolution: {integrity: sha512-qJhbOQy5VW5qzU74AimjAR9FRFQfrMa7dn4gkEXKMB/S9xZN8e1yC1uA9C15jkXI/PzmJ0muDIWmFwatm5/+VA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/img@0.0.11': resolution: {integrity: sha512-aGc8Y6U5C3igoMaqAJKsCpkbm1XjguQ09Acd+YcTKwjnC2+0w3yGUJkjWB2vTx4tN8dCqQCXO8FmdJpMfOA9EQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/link@0.0.12': resolution: {integrity: sha512-vF+xxQk2fGS1CN7UPQDbzvcBGfffr+GjTPNiWM38fhBfsLv6A/YUfaqxWlmL7zLzVmo0K2cvvV9wxlSyNba1aQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/markdown@0.0.14': resolution: {integrity: sha512-5IsobCyPkb4XwnQO8uFfGcNOxnsg3311GRXhJ3uKv51P7Jxme4ycC/MITnwIZ10w2zx7HIyTiqVzTj4XbuIHbg==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/preview@0.0.12': resolution: {integrity: sha512-g/H5fa9PQPDK6WUEG7iTlC19sAktI23qyoiJtMLqQiXFCfWeQMhqjLGKeLSKkfzszqmfJCjZtpSiKtBoOdxp3Q==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc @@ -2918,24 +2946,28 @@ packages: '@react-email/row@0.0.12': resolution: {integrity: sha512-HkCdnEjvK3o+n0y0tZKXYhIXUNPDx+2vq1dJTmqappVHXS5tXS6W5JOPZr5j+eoZ8gY3PShI2LWj5rWF7ZEtIQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/section@0.0.16': resolution: {integrity: sha512-FjqF9xQ8FoeUZYKSdt8sMIKvoT9XF8BrzhT3xiFKdEMwYNbsDflcjfErJe3jb7Wj/es/lKTbV5QR1dnLzGpL3w==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/tailwind@1.0.4': resolution: {integrity: sha512-tJdcusncdqgvTUYZIuhNC6LYTfL9vNTSQpwWdTCQhQ1lsrNCEE4OKCSdzSV3S9F32pi0i0xQ+YPJHKIzGjdTSA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc '@react-email/text@0.0.11': resolution: {integrity: sha512-a7nl/2KLpRHOYx75YbYZpWspUbX1DFY7JIZbOv5x0QU8SvwDbJt+Hm01vG34PffFyYvHEXrc6Qnip2RTjljNjg==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc @@ -3468,6 +3500,10 @@ packages: '@types/express@4.17.25': resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} + '@types/form-data@2.5.2': + resolution: {integrity: sha512-tfmcyHn1Pp9YHAO5r40+UuZUPAZbUEgqTel3EuEKpmF9hPkXgR4l41853raliXnb4gwyPNoQOfvgGGlHN5WSog==} + deprecated: This is a stub types definition. form-data provides its own type definitions, so you do not need this installed. + '@types/fs-extra@11.0.4': resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} @@ -3495,6 +3531,10 @@ packages: '@types/long@4.0.2': resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + '@types/marked@6.0.0': + resolution: {integrity: sha512-jmjpa4BwUsmhxcfsgUit/7A9KbrC48Q0q8KvnY107ogcjGgTFDlIL3RpihNpx2Mu1hM4mdFQjoVc4O6JoGKHsA==} + deprecated: This is a stub types definition. marked provides its own type definitions, so you do not need this installed. + '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -4212,6 +4252,7 @@ packages: basic-ftp@5.2.0: resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==} engines: {node: '>=10.0.0'} + deprecated: Security vulnerability fixed in 5.2.1, please upgrade better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} @@ -6373,6 +6414,11 @@ packages: engines: {node: '>= 18'} hasBin: true + marked@18.0.5: + resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==} + engines: {node: '>= 20'} + hasBin: true + marked@7.0.4: resolution: {integrity: sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==} engines: {node: '>= 16'} @@ -8279,10 +8325,12 @@ packages: uuid@10.0.0: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true vali-date@1.0.0: @@ -11417,6 +11465,10 @@ snapshots: '@types/qs': 6.14.0 '@types/serve-static': 1.15.10 + '@types/form-data@2.5.2': + dependencies: + form-data: 4.0.5 + '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 @@ -11444,6 +11496,10 @@ snapshots: '@types/long@4.0.2': {} + '@types/marked@6.0.0': + dependencies: + marked: 18.0.5 + '@types/mime@1.3.5': {} '@types/ms@2.1.0': @@ -14728,6 +14784,8 @@ snapshots: marked@14.0.0: {} + marked@18.0.5: {} + marked@7.0.4: {} math-intrinsics@1.1.0: {} @@ -17148,7 +17206,7 @@ snapshots: - supports-color - terser - vitest@2.1.9(@types/node@22.19.10)(@vitest/ui@4.0.18)(happy-dom@20.5.3)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0): + vitest@2.1.9(@types/node@22.19.10)(@vitest/ui@4.0.18(vitest@4.0.18))(happy-dom@20.5.3)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0): dependencies: '@vitest/expect': 2.1.9 '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@22.19.10)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)) @@ -17186,7 +17244,7 @@ snapshots: - supports-color - terser - vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.33)(@vitest/ui@4.0.18)(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.33)(@vitest/ui@4.0.18(vitest@4.0.18))(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 @@ -17231,7 +17289,7 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.19.10)(@vitest/ui@4.0.18)(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.19.10)(@vitest/ui@4.0.18(vitest@4.0.18))(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4