ModelContextProtocol (MCP) Full Compatibility (#3547)
* WIP MCP full compatibility layer * implement MCP agent function wrapping and invocation methods * Add `uvx` to docker bin for MCP executions * dev build * prune removed data * Wrap MCP servers to lazy load items to not block the UI Mobile bug fixes * arm64 test build * reset dev builder * remove unused prop
This commit is contained in:
parent
bd9b7e812d
commit
7a01298a8e
7
.github/workflows/dev-build.yaml
vendored
7
.github/workflows/dev-build.yaml
vendored
@ -6,7 +6,7 @@ concurrency:
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['3157-feat-prompt-variables'] # put your current branch to create a build. Core team only.
|
||||
branches: ['3000-mcp-compatibility'] # put your current branch to create a build. Core team only.
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'cloud-deployments/*'
|
||||
@ -43,6 +43,10 @@ jobs:
|
||||
fi
|
||||
id: dockerhub
|
||||
|
||||
# Uncomment this + add linux/arm64 to platforms if you want to build for arm64 as well
|
||||
# - name: Set up QEMU
|
||||
# uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
@ -74,6 +78,7 @@ jobs:
|
||||
sbom: true
|
||||
provenance: mode=max
|
||||
platforms: linux/amd64
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
|
||||
@ -24,10 +24,16 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
|
||||
apt-get update && \
|
||||
# Install node and yarn
|
||||
apt-get install -yq --no-install-recommends nodejs && \
|
||||
curl -LO https://github.com/yarnpkg/yarn/releases/download/v1.22.19/yarn_1.22.19_all.deb \
|
||||
&& dpkg -i yarn_1.22.19_all.deb \
|
||||
&& rm yarn_1.22.19_all.deb && \
|
||||
# Install uvx (pinned to 0.6.10) for MCP support
|
||||
curl -LsSf https://astral.sh/uv/0.6.10/install.sh | sh && \
|
||||
mv /root/.local/bin/uv /usr/local/bin/uv && \
|
||||
mv /root/.local/bin/uvx /usr/local/bin/uvx && \
|
||||
echo "Installed uvx! $(uv --version)" && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@ -84,10 +90,16 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
|
||||
apt-get update && \
|
||||
# Install node and yarn
|
||||
apt-get install -yq --no-install-recommends nodejs && \
|
||||
curl -LO https://github.com/yarnpkg/yarn/releases/download/v1.22.19/yarn_1.22.19_all.deb \
|
||||
&& dpkg -i yarn_1.22.19_all.deb \
|
||||
&& rm yarn_1.22.19_all.deb && \
|
||||
# Install uvx (pinned to 0.6.10) for MCP support
|
||||
curl -LsSf https://astral.sh/uv/0.6.10/install.sh | sh && \
|
||||
mv /root/.local/bin/uv /usr/local/bin/uv && \
|
||||
mv /root/.local/bin/uvx /usr/local/bin/uvx && \
|
||||
echo "Installed uvx! $(uv --version)" && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@ -154,12 +166,6 @@ RUN chown -R anythingllm:anythingllm /app/server && \
|
||||
chown -R anythingllm:anythingllm /app/collector
|
||||
USER anythingllm
|
||||
|
||||
# No longer needed? (deprecated)
|
||||
# WORKDIR /app/server
|
||||
# RUN npx prisma generate --schema=./prisma/schema.prisma && \
|
||||
# npx prisma migrate deploy --schema=./prisma/schema.prisma
|
||||
# WORKDIR /app
|
||||
|
||||
# Setup the environment
|
||||
ENV NODE_ENV=production
|
||||
ENV ANYTHING_LLM_RUNTIME=docker
|
||||
|
||||
1
frontend/src/media/agents/mcp-logo.svg
Normal file
1
frontend/src/media/agents/mcp-logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 184 195" width="184" height="195"><style>.a{fill:none;stroke:#fff;stroke-linecap:round;stroke-width:12}</style><path fill-rule="evenodd" class="a" d="m25 97.9l67.9-67.9c9.4-9.4 24.6-9.4 33.9 0 9.4 9.3 9.4 24.5 0 33.9l-51.2 51.3"/><path fill-rule="evenodd" class="a" d="m76.3 114.5l50.5-50.6c9.4-9.4 24.6-9.4 34 0l0.3 0.4c9.4 9.3 9.4 24.5 0 33.9l-61.4 61.4c-3.1 3.1-3.1 8.2 0 11.3l12.6 12.6"/><path fill-rule="evenodd" class="a" d="m109.9 46.9l-50.3 50.2c-9.3 9.4-9.3 24.6 0 34 9.4 9.4 24.6 9.4 34 0l50.2-50.2"/></svg>
|
||||
|
After Width: | Height: | Size: 584 B |
76
frontend/src/models/mcpServers.js
Normal file
76
frontend/src/models/mcpServers.js
Normal file
@ -0,0 +1,76 @@
|
||||
import { API_BASE } from "@/utils/constants";
|
||||
import { baseHeaders } from "@/utils/request";
|
||||
|
||||
const MCPServers = {
|
||||
/**
|
||||
* Forces a reload of the MCP Hypervisor and its servers
|
||||
* @returns {Promise<{success: boolean, error: string | null, servers: Array<{name: string, running: boolean, tools: Array<{name: string, description: string, inputSchema: Object}>, error: string | null, process: {pid: number, cmd: string} | null}>}>}
|
||||
*/
|
||||
forceReload: async () => {
|
||||
return await fetch(`${API_BASE}/mcp-servers/force-reload`, {
|
||||
method: "GET",
|
||||
headers: baseHeaders(),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.catch((e) => ({
|
||||
servers: [],
|
||||
success: false,
|
||||
error: e.message,
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* List all available MCP servers in the system
|
||||
* @returns {Promise<{success: boolean, error: string | null, servers: Array<{name: string, running: boolean, tools: Array<{name: string, description: string, inputSchema: Object}>, error: string | null, process: {pid: number, cmd: string} | null}>}>}
|
||||
*/
|
||||
listServers: async () => {
|
||||
return await fetch(`${API_BASE}/mcp-servers/list`, {
|
||||
method: "GET",
|
||||
headers: baseHeaders(),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.catch((e) => ({
|
||||
success: false,
|
||||
error: e.message,
|
||||
servers: [],
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the MCP server (start or stop)
|
||||
* @param {string} name - The name of the MCP server to toggle
|
||||
* @returns {Promise<{success: boolean, error: string | null}>}
|
||||
*/
|
||||
toggleServer: async (name) => {
|
||||
return await fetch(`${API_BASE}/mcp-servers/toggle`, {
|
||||
method: "POST",
|
||||
headers: baseHeaders(),
|
||||
body: JSON.stringify({ name }),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.catch((e) => ({
|
||||
success: false,
|
||||
error: e.message,
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete the MCP server - will also remove it from the config file
|
||||
* @param {string} name - The name of the MCP server to delete
|
||||
* @returns {Promise<{success: boolean, error: string | null}>}
|
||||
*/
|
||||
deleteServer: async (name) => {
|
||||
return await fetch(`${API_BASE}/mcp-servers/delete`, {
|
||||
method: "POST",
|
||||
headers: baseHeaders(),
|
||||
body: JSON.stringify({ name }),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.catch((e) => ({
|
||||
success: false,
|
||||
error: e.message,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
export default MCPServers;
|
||||
@ -22,7 +22,7 @@ export default function AgentFlowsList({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-theme-bg-secondary text-white rounded-xl min-w-[360px] w-fit">
|
||||
<div className="bg-theme-bg-secondary text-white rounded-xl w-full md:min-w-[360px]">
|
||||
{flows.map((flow, index) => (
|
||||
<div
|
||||
key={flow.uuid}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { CaretRight } from "@phosphor-icons/react";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { sentenceCase } from "text-case";
|
||||
|
||||
export default function ImportedSkillList({
|
||||
@ -27,9 +26,7 @@ export default function ImportedSkillList({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`bg-theme-bg-secondary text-white rounded-xl ${
|
||||
isMobile ? "w-full" : "min-w-[360px] w-fit"
|
||||
}`}
|
||||
className={`bg-theme-bg-secondary text-white rounded-xl w-full md:min-w-[360px]`}
|
||||
>
|
||||
{skills.map((config, index) => (
|
||||
<div
|
||||
|
||||
240
frontend/src/pages/Admin/Agents/MCPServers/ServerPanel.jsx
Normal file
240
frontend/src/pages/Admin/Agents/MCPServers/ServerPanel.jsx
Normal file
@ -0,0 +1,240 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import showToast from "@/utils/toast";
|
||||
import { CaretDown, Gear } from "@phosphor-icons/react";
|
||||
import MCPLogo from "@/media/agents/mcp-logo.svg";
|
||||
import { titleCase } from "text-case";
|
||||
import truncate from "truncate";
|
||||
import MCPServers from "@/models/mcpServers";
|
||||
import pluralize from "pluralize";
|
||||
|
||||
function ManageServerMenu({ server, toggleServer, onDelete }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [running, setRunning] = useState(server.running);
|
||||
const menuRef = useRef(null);
|
||||
|
||||
async function deleteServer() {
|
||||
if (
|
||||
!window.confirm(
|
||||
"Are you sure you want to delete this MCP server? It will be removed from your config file and you will need to add it back manually."
|
||||
)
|
||||
)
|
||||
return;
|
||||
const { success, error } = await MCPServers.deleteServer(server.name);
|
||||
if (success) {
|
||||
showToast("MCP server deleted successfully.", "success");
|
||||
onDelete(server.name);
|
||||
} else {
|
||||
showToast(error || "Failed to delete MCP server.", "error");
|
||||
}
|
||||
}
|
||||
|
||||
async function handleToggleServer() {
|
||||
if (
|
||||
!window.confirm(
|
||||
running
|
||||
? "Are you sure you want to stop this MCP server? It will be started automatically when you next start the server."
|
||||
: "Are you sure you want to start this MCP server? It will be started automatically when you next start the server."
|
||||
)
|
||||
)
|
||||
return;
|
||||
|
||||
const { success, error } = await MCPServers.toggleServer(server.name);
|
||||
if (success) {
|
||||
setRunning(!running);
|
||||
toggleServer(server.name);
|
||||
showToast(
|
||||
`MCP server ${server.name} ${running ? "started" : "stopped"} successfully.`,
|
||||
"success",
|
||||
{ clear: true }
|
||||
);
|
||||
} else {
|
||||
showToast(error || "Failed to toggle MCP server.", "error", {
|
||||
clear: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
||||
setOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="relative" ref={menuRef}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(!open)}
|
||||
className="p-1.5 rounded-lg text-white hover:bg-theme-action-menu-item-hover transition-colors duration-300"
|
||||
>
|
||||
<Gear className="h-5 w-5" weight="bold" />
|
||||
</button>
|
||||
{open && (
|
||||
<div className="absolute w-[150px] top-1 left-7 mt-1 border-[1.5px] border-white/40 rounded-lg bg-theme-action-menu-bg flex flex-col shadow-[0_4px_14px_rgba(0,0,0,0.25)] text-white z-99 md:z-10">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleToggleServer}
|
||||
className="border-none flex items-center rounded-lg gap-x-2 hover:bg-theme-action-menu-item-hover py-1.5 px-2 transition-colors duration-200 w-full text-left"
|
||||
>
|
||||
<span className="text-sm">
|
||||
{running ? "Stop MCP Server" : "Start MCP Server"}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={deleteServer}
|
||||
className="border-none flex items-center rounded-lg gap-x-2 hover:bg-theme-action-menu-item-hover py-1.5 px-2 transition-colors duration-200 w-full text-left"
|
||||
>
|
||||
<span className="text-sm">Delete MCP Server</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ServerPanel({ server, toggleServer, onDelete }) {
|
||||
return (
|
||||
<>
|
||||
<div className="p-2">
|
||||
<div className="flex flex-col gap-y-[18px] max-w-[800px]">
|
||||
<div className="flex w-full justify-between">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<img src={MCPLogo} className="w-6 h-6 light:invert" />
|
||||
<label htmlFor="name" className="text-white text-md font-bold">
|
||||
{titleCase(server.name.replace(/[_-]/g, " "))}
|
||||
</label>
|
||||
{server.tools.length > 0 && (
|
||||
<p className="text-theme-text-secondary text-sm">
|
||||
{server.tools.length} {pluralize("tool", server.tools.length)}{" "}
|
||||
available
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<ManageServerMenu
|
||||
key={server.name}
|
||||
server={server}
|
||||
toggleServer={toggleServer}
|
||||
onDelete={onDelete}
|
||||
/>
|
||||
</div>
|
||||
<RenderServerConfig config={server.config} />
|
||||
<RenderServerStatus server={server} />
|
||||
<RenderServerTools tools={server.tools} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function RenderServerConfig({ config = null }) {
|
||||
if (!config) return null;
|
||||
return (
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<p className="text-theme-text-primary text-sm">Startup Command</p>
|
||||
<div className="bg-theme-bg-primary rounded-lg p-4">
|
||||
<p className="text-theme-text-secondary text-sm text-left">
|
||||
<span className="font-bold">Command:</span> {config.command}
|
||||
</p>
|
||||
<p className="text-theme-text-secondary text-sm text-left">
|
||||
<span className="font-bold">Arguments:</span>{" "}
|
||||
{config.args ? config.args.join(" ") : "None"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RenderServerStatus({ server }) {
|
||||
if (server.running || !server.error) return null;
|
||||
return (
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<p className="text-theme-text-primary text-sm">
|
||||
This MCP server is not running - it may be stopped or experiencing an
|
||||
error on startup.
|
||||
</p>
|
||||
<div className="bg-theme-bg-primary rounded-lg p-4">
|
||||
<p className="text-red-500 text-sm font-mono">{server.error}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RenderServerTools({ tools = [] }) {
|
||||
if (tools.length === 0) return null;
|
||||
return (
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<div className="flex flex-col gap-y-2">
|
||||
{tools.map((tool) => (
|
||||
<ServerTool key={tool.name} tool={tool} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ServerTool({ tool }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(!open)}
|
||||
className="flex flex-col gap-y-2 px-4 py-2 rounded-lg border border-theme-text-secondary"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<p className="text-theme-text-primary font-mono font-bold text-sm">
|
||||
{tool.name}
|
||||
</p>
|
||||
{!open && (
|
||||
<p className="text-theme-text-secondary text-sm">
|
||||
{truncate(tool.description, 70)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="border-none text-theme-text-secondary hover:text-cta-button">
|
||||
<CaretDown size={16} />
|
||||
</div>
|
||||
</div>
|
||||
{open && (
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<p className="text-theme-text-secondary text-sm text-left">
|
||||
{tool.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<p className="text-theme-text-primary text-sm text-left">
|
||||
Tool call arguments
|
||||
</p>
|
||||
<div className="flex flex-col gap-y-2">
|
||||
{Object.entries(tool.inputSchema?.properties || {}).map(
|
||||
([key, value]) => (
|
||||
<div key={key} className="flex items-center gap-x-2">
|
||||
<p className="text-theme-text-secondary text-sm text-left font-bold">
|
||||
{key}
|
||||
{tool.inputSchema?.required?.includes(key) && (
|
||||
<sup className="text-red-500">*</sup>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-theme-text-secondary text-sm text-left">
|
||||
{value.type}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
147
frontend/src/pages/Admin/Agents/MCPServers/index.jsx
Normal file
147
frontend/src/pages/Admin/Agents/MCPServers/index.jsx
Normal file
@ -0,0 +1,147 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { titleCase } from "text-case";
|
||||
import { Link } from "react-router-dom";
|
||||
import { BookOpenText, ArrowClockwise } from "@phosphor-icons/react";
|
||||
import MCPLogo from "@/media/agents/mcp-logo.svg";
|
||||
import MCPServers from "@/models/mcpServers";
|
||||
|
||||
export function MCPServerHeader({ setMcpServers, children }) {
|
||||
const [loadingMcpServers, setLoadingMcpServers] = useState(false);
|
||||
useEffect(() => {
|
||||
async function fetchMCPServers() {
|
||||
setLoadingMcpServers(true);
|
||||
const { servers = [] } = await MCPServers.listServers();
|
||||
setMcpServers(servers);
|
||||
setLoadingMcpServers(false);
|
||||
}
|
||||
fetchMCPServers();
|
||||
}, []);
|
||||
|
||||
// Refresh the list of MCP servers
|
||||
const refreshMCPServers = () => {
|
||||
if (
|
||||
window.confirm(
|
||||
"Are you sure you want to refresh the list of MCP servers? This will restart all MCP servers and reload their tools."
|
||||
)
|
||||
) {
|
||||
setLoadingMcpServers(true);
|
||||
MCPServers.forceReload()
|
||||
.then(({ servers = [] }) => {
|
||||
setSelectedMcpServer(null);
|
||||
setMcpServers(servers);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
showToast(`Failed to refresh MCP servers.`, "error", { clear: true });
|
||||
})
|
||||
.finally(() => {
|
||||
setLoadingMcpServers(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="text-theme-text-primary flex items-center justify-between gap-x-2 mt-4">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<img src={MCPLogo} className="w-6 h-6 light:invert" />
|
||||
<p className="text-lg font-medium">MCP Servers</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-3">
|
||||
<Link
|
||||
to="#goes-to-docs"
|
||||
target="_blank"
|
||||
className="border-none text-theme-text-secondary hover:text-cta-button"
|
||||
>
|
||||
<BookOpenText size={16} />
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
onClick={refreshMCPServers}
|
||||
disabled={loadingMcpServers}
|
||||
className="border-none text-theme-text-secondary hover:text-cta-button flex items-center gap-x-1"
|
||||
>
|
||||
<ArrowClockwise
|
||||
size={16}
|
||||
className={loadingMcpServers ? "animate-spin" : ""}
|
||||
/>
|
||||
<p className="text-sm">
|
||||
{loadingMcpServers ? "Loading..." : "Refresh"}
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{children({ loadingMcpServers })}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function MCPServersList({
|
||||
isLoading = false,
|
||||
servers = [],
|
||||
selectedServer,
|
||||
handleClick,
|
||||
}) {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="text-theme-text-secondary text-center text-xs flex flex-col gap-y-2">
|
||||
<p>Loading MCP Servers from configuration file...</p>
|
||||
<a
|
||||
href="https://docs.anythingllm.com/mcp-servers/getting-started"
|
||||
target="_blank"
|
||||
className="text-theme-text-secondary underline hover:text-cta-button"
|
||||
>
|
||||
Learn more about MCP Servers.
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (servers.length === 0) {
|
||||
return (
|
||||
<div className="text-theme-text-secondary text-center text-xs flex flex-col gap-y-2">
|
||||
<p>No MCP servers found</p>
|
||||
<a
|
||||
href="https://docs.anythingllm.com/mcp-servers/getting-started"
|
||||
target="_blank"
|
||||
className="text-theme-text-secondary underline hover:text-cta-button"
|
||||
>
|
||||
Learn more about MCP Servers.
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-theme-bg-secondary text-white rounded-xl w-full md:min-w-[360px]">
|
||||
{servers.map((server, index) => (
|
||||
<div
|
||||
key={server.name}
|
||||
className={`py-3 px-4 flex items-center justify-between ${
|
||||
index === 0 ? "rounded-t-xl" : ""
|
||||
} ${
|
||||
index === servers.length - 1
|
||||
? "rounded-b-xl"
|
||||
: "border-b border-white/10"
|
||||
} cursor-pointer transition-all duration-300 hover:bg-theme-bg-primary ${
|
||||
selectedServer?.name === server.name
|
||||
? "bg-white/10 light:bg-theme-bg-sidebar"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => handleClick?.(server)}
|
||||
>
|
||||
<div className="text-sm font-light">
|
||||
{titleCase(server.name.replace(/[_-]/g, " "))}
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div
|
||||
className={`text-sm text-theme-text-secondary font-medium ${server.running ? "text-green-500" : "text-red-500"}`}
|
||||
>
|
||||
{server.running ? "On" : "Stopped"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -11,7 +11,6 @@ import {
|
||||
Robot,
|
||||
Hammer,
|
||||
FlowArrow,
|
||||
PlusCircle,
|
||||
} from "@phosphor-icons/react";
|
||||
import ContextualSaveBar from "@/components/ContextualSaveBar";
|
||||
import { castToType } from "@/utils/types";
|
||||
@ -23,6 +22,8 @@ import ImportedSkillConfig from "./Imported/ImportedSkillConfig";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import AgentFlowsList from "./AgentFlows";
|
||||
import FlowPanel from "./AgentFlows/FlowPanel";
|
||||
import { MCPServersList, MCPServerHeader } from "./MCPServers";
|
||||
import ServerPanel from "./MCPServers/ServerPanel";
|
||||
import { Link } from "react-router-dom";
|
||||
import paths from "@/utils/paths";
|
||||
import AgentFlows from "@/models/agentFlows";
|
||||
@ -43,6 +44,10 @@ export default function AdminAgents() {
|
||||
const [selectedFlow, setSelectedFlow] = useState(null);
|
||||
const [activeFlowIds, setActiveFlowIds] = useState([]);
|
||||
|
||||
// MCP Servers are lazy loaded to not block the UI thread
|
||||
const [mcpServers, setMcpServers] = useState([]);
|
||||
const [selectedMcpServer, setSelectedMcpServer] = useState(null);
|
||||
|
||||
// Alert user if they try to leave the page with unsaved changes
|
||||
useEffect(() => {
|
||||
const handleBeforeUnload = (event) => {
|
||||
@ -67,6 +72,7 @@ export default function AdminAgents() {
|
||||
"active_agent_flows",
|
||||
]);
|
||||
const { flows = [] } = await AgentFlows.listFlows();
|
||||
|
||||
setSettings({ ..._settings, preferences: _preferences.settings } ?? {});
|
||||
setAgentSkills(_preferences.settings?.default_agent_skills ?? []);
|
||||
setDisabledAgentSkills(
|
||||
@ -109,6 +115,15 @@ export default function AdminAgents() {
|
||||
});
|
||||
};
|
||||
|
||||
const toggleMCP = (serverName) => {
|
||||
setMcpServers((prev) => {
|
||||
return prev.map((server) => {
|
||||
if (server.name !== serverName) return server;
|
||||
return { ...server, running: !server.running };
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const data = {
|
||||
@ -159,23 +174,46 @@ export default function AdminAgents() {
|
||||
setHasChanges(false);
|
||||
};
|
||||
|
||||
const SelectedSkillComponent = selectedFlow
|
||||
? FlowPanel
|
||||
: selectedSkill?.imported
|
||||
? ImportedSkillConfig
|
||||
: configurableSkills[selectedSkill]?.component ||
|
||||
defaultSkills[selectedSkill]?.component;
|
||||
let SelectedSkillComponent = null;
|
||||
if (selectedFlow) {
|
||||
SelectedSkillComponent = FlowPanel;
|
||||
} else if (selectedMcpServer) {
|
||||
SelectedSkillComponent = ServerPanel;
|
||||
} else if (selectedSkill?.imported) {
|
||||
SelectedSkillComponent = ImportedSkillConfig;
|
||||
} else if (configurableSkills[selectedSkill]) {
|
||||
SelectedSkillComponent = configurableSkills[selectedSkill]?.component;
|
||||
} else {
|
||||
SelectedSkillComponent = defaultSkills[selectedSkill]?.component;
|
||||
}
|
||||
|
||||
// Update the click handlers to clear the other selection
|
||||
const handleDefaultSkillClick = (skill) => {
|
||||
setSelectedFlow(null);
|
||||
setSelectedMcpServer(null);
|
||||
setSelectedSkill(skill);
|
||||
if (isMobile) setShowSkillModal(true);
|
||||
};
|
||||
|
||||
const handleSkillClick = (skill) => {
|
||||
setSelectedFlow(null);
|
||||
setSelectedMcpServer(null);
|
||||
setSelectedSkill(skill);
|
||||
if (isMobile) setShowSkillModal(true);
|
||||
};
|
||||
|
||||
const handleFlowClick = (flow) => {
|
||||
setSelectedSkill(null);
|
||||
setSelectedMcpServer(null);
|
||||
setSelectedFlow(flow);
|
||||
if (isMobile) setShowSkillModal(true);
|
||||
};
|
||||
|
||||
const handleMCPClick = (server) => {
|
||||
setSelectedSkill(null);
|
||||
setSelectedFlow(null);
|
||||
setSelectedMcpServer(server);
|
||||
if (isMobile) setShowSkillModal(true);
|
||||
};
|
||||
|
||||
const handleFlowDelete = (flowId) => {
|
||||
@ -184,6 +222,13 @@ export default function AdminAgents() {
|
||||
setAgentFlows((prev) => prev.filter((flow) => flow.uuid !== flowId));
|
||||
};
|
||||
|
||||
const handleMCPServerDelete = (serverName) => {
|
||||
setSelectedMcpServer(null);
|
||||
setMcpServers((prev) =>
|
||||
prev.filter((server) => server.name !== serverName)
|
||||
);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div
|
||||
@ -220,7 +265,10 @@ export default function AdminAgents() {
|
||||
/>
|
||||
|
||||
{/* Skill settings nav */}
|
||||
<div hidden={showSkillModal} className="flex flex-col gap-y-[18px]">
|
||||
<div
|
||||
hidden={showSkillModal}
|
||||
className="flex flex-col gap-y-[18px] overflow-y-scroll no-scroll"
|
||||
>
|
||||
<div className="text-theme-text-primary flex items-center gap-x-2">
|
||||
<Robot size={24} />
|
||||
<p className="text-lg font-medium">Agent Skills</p>
|
||||
@ -229,11 +277,7 @@ export default function AdminAgents() {
|
||||
<SkillList
|
||||
skills={defaultSkills}
|
||||
selectedSkill={selectedSkill}
|
||||
handleClick={(skill) => {
|
||||
setSelectedFlow(null);
|
||||
setSelectedSkill(skill);
|
||||
setShowSkillModal(true);
|
||||
}}
|
||||
handleClick={handleDefaultSkillClick}
|
||||
activeSkills={Object.keys(defaultSkills).filter(
|
||||
(skill) => !disabledAgentSkills.includes(skill)
|
||||
)}
|
||||
@ -242,11 +286,7 @@ export default function AdminAgents() {
|
||||
<SkillList
|
||||
skills={configurableSkills}
|
||||
selectedSkill={selectedSkill}
|
||||
handleClick={(skill) => {
|
||||
setSelectedFlow(null);
|
||||
setSelectedSkill(skill);
|
||||
setShowSkillModal(true);
|
||||
}}
|
||||
handleClick={handleDefaultSkillClick}
|
||||
activeSkills={agentSkills}
|
||||
/>
|
||||
|
||||
@ -275,6 +315,18 @@ export default function AdminAgents() {
|
||||
id="active_agent_flows"
|
||||
value={activeFlowIds.join(",")}
|
||||
/>
|
||||
<MCPServerHeader setMcpServers={setMcpServers}>
|
||||
{({ loadingMcpServers }) => {
|
||||
return (
|
||||
<MCPServersList
|
||||
isLoading={loadingMcpServers}
|
||||
servers={mcpServers}
|
||||
selectedServer={selectedMcpServer}
|
||||
handleClick={handleMCPClick}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</MCPServerHeader>
|
||||
</div>
|
||||
|
||||
{/* Selected agent skill modal */}
|
||||
@ -297,10 +349,16 @@ export default function AdminAgents() {
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto p-4">
|
||||
<div className=" bg-theme-bg-secondary text-white rounded-xl p-4">
|
||||
<div className=" bg-theme-bg-secondary text-white rounded-xl p-4 overflow-y-scroll no-scroll">
|
||||
{SelectedSkillComponent ? (
|
||||
<>
|
||||
{selectedFlow ? (
|
||||
{selectedMcpServer ? (
|
||||
<ServerPanel
|
||||
server={selectedMcpServer}
|
||||
toggleServer={toggleMCP}
|
||||
onDelete={handleMCPServerDelete}
|
||||
/>
|
||||
) : selectedFlow ? (
|
||||
<FlowPanel
|
||||
flow={selectedFlow}
|
||||
toggleFlow={toggleFlow}
|
||||
@ -349,7 +407,7 @@ export default function AdminAgents() {
|
||||
<div className="flex flex-col items-center justify-center h-full text-theme-text-secondary">
|
||||
<Robot size={40} />
|
||||
<p className="font-medium">
|
||||
Select an agent skill or flow
|
||||
Select an Agent Skill, Agent Flow, or MCP Server
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@ -460,16 +518,35 @@ export default function AdminAgents() {
|
||||
selectedFlow={selectedFlow}
|
||||
handleClick={handleFlowClick}
|
||||
/>
|
||||
|
||||
<MCPServerHeader setMcpServers={setMcpServers}>
|
||||
{({ loadingMcpServers }) => {
|
||||
return (
|
||||
<MCPServersList
|
||||
isLoading={loadingMcpServers}
|
||||
servers={mcpServers}
|
||||
selectedServer={selectedMcpServer}
|
||||
handleClick={handleMCPClick}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</MCPServerHeader>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Selected agent skill setting panel */}
|
||||
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||
<div className="bg-theme-bg-secondary text-white rounded-xl flex-1 p-4">
|
||||
<div className="bg-theme-bg-secondary text-white rounded-xl flex-1 p-4 overflow-y-scroll no-scroll">
|
||||
{SelectedSkillComponent ? (
|
||||
<>
|
||||
{selectedFlow ? (
|
||||
{selectedMcpServer ? (
|
||||
<ServerPanel
|
||||
server={selectedMcpServer}
|
||||
toggleServer={toggleMCP}
|
||||
onDelete={handleMCPServerDelete}
|
||||
/>
|
||||
) : selectedFlow ? (
|
||||
<FlowPanel
|
||||
flow={selectedFlow}
|
||||
toggleFlow={toggleFlow}
|
||||
@ -517,7 +594,9 @@ export default function AdminAgents() {
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center h-full text-theme-text-secondary">
|
||||
<Robot size={40} />
|
||||
<p className="font-medium">Select an agent skill or flow</p>
|
||||
<p className="font-medium">
|
||||
Select an Agent Skill, Agent Flow, or MCP Server
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
1
server/.gitignore
vendored
1
server/.gitignore
vendored
@ -10,6 +10,7 @@ storage/exports
|
||||
storage/imports
|
||||
storage/plugins/agent-skills/*
|
||||
storage/plugins/agent-flows/*
|
||||
storage/plugins/anythingllm_mcp_servers.json
|
||||
!storage/documents/DOCUMENTS.md
|
||||
logs/server.log
|
||||
*.db
|
||||
|
||||
100
server/endpoints/mcpServers.js
Normal file
100
server/endpoints/mcpServers.js
Normal file
@ -0,0 +1,100 @@
|
||||
const { reqBody } = require("../utils/http");
|
||||
const MCPCompatibilityLayer = require("../utils/MCP");
|
||||
const {
|
||||
flexUserRoleValid,
|
||||
ROLES,
|
||||
} = require("../utils/middleware/multiUserProtected");
|
||||
const { validatedRequest } = require("../utils/middleware/validatedRequest");
|
||||
|
||||
function mcpServersEndpoints(app) {
|
||||
if (!app) return;
|
||||
|
||||
app.get(
|
||||
"/mcp-servers/force-reload",
|
||||
[validatedRequest, flexUserRoleValid([ROLES.admin])],
|
||||
async (_request, response) => {
|
||||
try {
|
||||
const mcp = new MCPCompatibilityLayer();
|
||||
await mcp.reloadMCPServers();
|
||||
return response.status(200).json({
|
||||
success: true,
|
||||
error: null,
|
||||
servers: await mcp.servers(),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error force reloading MCP servers:", error);
|
||||
return response.status(500).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
servers: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
app.get(
|
||||
"/mcp-servers/list",
|
||||
[validatedRequest, flexUserRoleValid([ROLES.admin])],
|
||||
async (_request, response) => {
|
||||
try {
|
||||
const servers = await new MCPCompatibilityLayer().servers();
|
||||
return response.status(200).json({
|
||||
success: true,
|
||||
servers,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error listing MCP servers:", error);
|
||||
return response.status(500).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
app.post(
|
||||
"/mcp-servers/toggle",
|
||||
[validatedRequest, flexUserRoleValid([ROLES.admin])],
|
||||
async (request, response) => {
|
||||
try {
|
||||
const { name } = reqBody(request);
|
||||
const result = await new MCPCompatibilityLayer().toggleServerStatus(
|
||||
name
|
||||
);
|
||||
return response.status(200).json({
|
||||
success: result.success,
|
||||
error: result.error,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error toggling MCP server:", error);
|
||||
return response.status(500).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
app.post(
|
||||
"/mcp-servers/delete",
|
||||
[validatedRequest, flexUserRoleValid([ROLES.admin])],
|
||||
async (request, response) => {
|
||||
try {
|
||||
const { name } = reqBody(request);
|
||||
const result = await new MCPCompatibilityLayer().deleteServer(name);
|
||||
return response.status(200).json({
|
||||
success: result.success,
|
||||
error: result.error,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error deleting MCP server:", error);
|
||||
return response.status(500).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = { mcpServersEndpoints };
|
||||
@ -27,6 +27,7 @@ const { experimentalEndpoints } = require("./endpoints/experimental");
|
||||
const { browserExtensionEndpoints } = require("./endpoints/browserExtension");
|
||||
const { communityHubEndpoints } = require("./endpoints/communityHub");
|
||||
const { agentFlowEndpoints } = require("./endpoints/agentFlows");
|
||||
const { mcpServersEndpoints } = require("./endpoints/mcpServers");
|
||||
const app = express();
|
||||
const apiRouter = express.Router();
|
||||
const FILE_LIMIT = "3GB";
|
||||
@ -63,6 +64,7 @@ experimentalEndpoints(apiRouter);
|
||||
developerEndpoints(app, apiRouter);
|
||||
communityHubEndpoints(apiRouter);
|
||||
agentFlowEndpoints(apiRouter);
|
||||
mcpServersEndpoints(apiRouter);
|
||||
|
||||
// Externally facing embedder endpoints
|
||||
embeddedEndpoints(apiRouter);
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
"@langchain/textsplitters": "0.0.0",
|
||||
"@mintplex-labs/bree": "^9.2.5",
|
||||
"@mintplex-labs/express-ws": "^5.0.7",
|
||||
"@modelcontextprotocol/sdk": "^1.8.0",
|
||||
"@pinecone-database/pinecone": "^2.0.1",
|
||||
"@prisma/client": "5.3.1",
|
||||
"@qdrant/js-client-rest": "^1.9.0",
|
||||
|
||||
322
server/utils/MCP/hypervisor/index.js
Normal file
322
server/utils/MCP/hypervisor/index.js
Normal file
@ -0,0 +1,322 @@
|
||||
const { safeJsonParse } = require("../../http");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const { Client } = require("@modelcontextprotocol/sdk/client/index.js");
|
||||
const {
|
||||
StdioClientTransport,
|
||||
} = require("@modelcontextprotocol/sdk/client/stdio.js");
|
||||
|
||||
/**
|
||||
* @class MCPHypervisor
|
||||
* @description A class that manages MCP servers found in the storage/plugins/anythingllm_mcp_servers.json file.
|
||||
* This class is responsible for booting, stopping, and reloading MCP servers - it is the user responsiblity for the MCP server definitions
|
||||
* to me correct and also functioning tools depending on their deployment (docker vs local) as well as the security of said tools
|
||||
* since MCP is basically arbitrary code execution.
|
||||
*
|
||||
* @notice This class is a singleton.
|
||||
* @notice Each MCP tool has dependencies specific to it and this call WILL NOT check for them.
|
||||
* For example, if the tools requires `npx` then the context in which AnythingLLM mains process is running will need to access npx.
|
||||
* This is typically not common in our pre-built image so may not function. But this is the case anywhere MCP is used.
|
||||
*
|
||||
* AnythingLLM will take care of porting MCP servers to agent-callable functions via @agent directive.
|
||||
* @see MCPCompatibilityLayer.convertServerToolsToPlugins
|
||||
*/
|
||||
class MCPHypervisor {
|
||||
static _instance;
|
||||
/**
|
||||
* The path to the JSON file containing the MCP server definitions.
|
||||
* @type {string}
|
||||
*/
|
||||
mcpServerJSONPath;
|
||||
|
||||
/**
|
||||
* The MCP servers currently running.
|
||||
* @type { { [key: string]: Client & {transport: {_process: import('child_process').ChildProcess}, aibitatToolIds: string[]} } }
|
||||
*/
|
||||
mcps = {};
|
||||
/**
|
||||
* The results of the MCP server loading process.
|
||||
* @type { { [key: string]: {status: 'success' | 'failed', message: string} } }
|
||||
*/
|
||||
mcpLoadingResults = {};
|
||||
|
||||
constructor() {
|
||||
if (MCPHypervisor._instance) return MCPHypervisor._instance;
|
||||
MCPHypervisor._instance = this;
|
||||
this.log("Initializing MCP Hypervisor - subsequent calls will boot faster");
|
||||
this.#setupConfigFile();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the MCP server definitions file.
|
||||
* Will create the file/directory if it doesn't exist already in storage/plugins with blank options
|
||||
*/
|
||||
#setupConfigFile() {
|
||||
this.mcpServerJSONPath =
|
||||
process.env.NODE_ENV === "development"
|
||||
? path.resolve(
|
||||
__dirname,
|
||||
`../../../storage/plugins/anythingllm_mcp_servers.json`
|
||||
)
|
||||
: path.resolve(
|
||||
process.env.STORAGE_DIR ??
|
||||
path.resolve(__dirname, `../../../storage`),
|
||||
`plugins/anythingllm_mcp_servers.json`
|
||||
);
|
||||
|
||||
if (!fs.existsSync(this.mcpServerJSONPath)) {
|
||||
fs.mkdirSync(path.dirname(this.mcpServerJSONPath), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
this.mcpServerJSONPath,
|
||||
JSON.stringify({ mcpServers: {} }, null, 2),
|
||||
{ encoding: "utf8" }
|
||||
);
|
||||
}
|
||||
|
||||
this.log(`MCP Config File: ${this.mcpServerJSONPath}`);
|
||||
}
|
||||
|
||||
log(text, ...args) {
|
||||
console.log(`\x1b[36m[${this.constructor.name}]\x1b[0m ${text}`, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MCP servers from the JSON file.
|
||||
* @returns { { name: string, server: { command: string, args: string[], env: { [key: string]: string } } }[] } The MCP servers.
|
||||
*/
|
||||
get mcpServerConfigs() {
|
||||
const servers = safeJsonParse(
|
||||
fs.readFileSync(this.mcpServerJSONPath, "utf8"),
|
||||
{ mcpServers: {} }
|
||||
);
|
||||
return Object.entries(servers.mcpServers).map(([name, server]) => ({
|
||||
name,
|
||||
server,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the MCP server from the config file
|
||||
* @param {string} name - The name of the MCP server to remove
|
||||
* @returns {boolean} - True if the MCP server was removed, false otherwise
|
||||
*/
|
||||
removeMCPServerFromConfig(name) {
|
||||
const servers = safeJsonParse(
|
||||
fs.readFileSync(this.mcpServerJSONPath, "utf8"),
|
||||
{ mcpServers: {} }
|
||||
);
|
||||
if (!servers.mcpServers[name]) return false;
|
||||
|
||||
delete servers.mcpServers[name];
|
||||
fs.writeFileSync(
|
||||
this.mcpServerJSONPath,
|
||||
JSON.stringify(servers, null, 2),
|
||||
"utf8"
|
||||
);
|
||||
this.log(`MCP server ${name} removed from config file`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the MCP servers - can be used to reload the MCP servers without restarting the server or app
|
||||
* and will also apply changes to the config file if any where made.
|
||||
*/
|
||||
async reloadMCPServers() {
|
||||
this.pruneMCPServers();
|
||||
await this.bootMCPServers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a single MCP server by its server name - public method
|
||||
* @param {string} name - The name of the MCP server to start
|
||||
* @returns {Promise<{success: boolean, error: string | null}>}
|
||||
*/
|
||||
async startMCPServer(name) {
|
||||
if (this.mcps[name])
|
||||
return { success: false, error: `MCP server ${name} already running` };
|
||||
const config = this.mcpServerConfigs.find((s) => s.name === name);
|
||||
if (!config)
|
||||
return {
|
||||
success: false,
|
||||
error: `MCP server ${name} not found in config file`,
|
||||
};
|
||||
|
||||
try {
|
||||
await this.#startMCPServer(config);
|
||||
this.mcpLoadingResults[name] = {
|
||||
status: "success",
|
||||
message: `Successfully connected to MCP server: ${name}`,
|
||||
};
|
||||
|
||||
return { success: true, message: `MCP server ${name} started` };
|
||||
} catch (e) {
|
||||
this.log(`Failed to start single MCP server: ${name}`, {
|
||||
error: e.message,
|
||||
code: e.code,
|
||||
syscall: e.syscall,
|
||||
path: e.path,
|
||||
stack: e.stack,
|
||||
});
|
||||
this.mcpLoadingResults[name] = {
|
||||
status: "failed",
|
||||
message: `Failed to start MCP server: ${name} [${e.code || "NO_CODE"}] ${e.message}`,
|
||||
};
|
||||
|
||||
// Clean up failed connection
|
||||
if (this.mcps[name]) {
|
||||
this.mcps[name].close();
|
||||
delete this.mcps[name];
|
||||
}
|
||||
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Prune a single MCP server by its server name
|
||||
* @param {string} name - The name of the MCP server to prune
|
||||
* @returns {boolean} - True if the MCP server was pruned, false otherwise
|
||||
*/
|
||||
pruneMCPServer(name) {
|
||||
if (!name || !this.mcps[name]) return true;
|
||||
|
||||
this.log(`Pruning MCP server: ${name}`);
|
||||
const mcp = this.mcps[name];
|
||||
const childProcess = mcp.transport._process;
|
||||
if (childProcess) childProcess.kill(1);
|
||||
mcp.transport.close();
|
||||
|
||||
delete this.mcps[name];
|
||||
this.mcpLoadingResults[name] = {
|
||||
status: "failed",
|
||||
message: `Server was stopped manually by the administrator.`,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prune the MCP servers - pkills and forgets all MCP servers
|
||||
* @returns {void}
|
||||
*/
|
||||
pruneMCPServers() {
|
||||
this.log(`Pruning ${Object.keys(this.mcps).length} MCP servers...`);
|
||||
|
||||
for (const name of Object.keys(this.mcps)) {
|
||||
if (!this.mcps[name]) continue;
|
||||
const mcp = this.mcps[name];
|
||||
const childProcess = mcp.transport._process;
|
||||
if (childProcess)
|
||||
this.log(`Killing MCP ${name} (PID: ${childProcess.pid})`, {
|
||||
killed: childProcess.kill(1),
|
||||
});
|
||||
|
||||
mcp.transport.close();
|
||||
mcp.close();
|
||||
}
|
||||
this.mcps = {};
|
||||
this.mcpLoadingResults = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private Start a single MCP server by its server definition from the JSON file
|
||||
* @param {string} name - The name of the MCP server to start
|
||||
* @param {Object} server - The server definition
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async #startMCPServer({ name, server }) {
|
||||
if (!name) throw new Error("MCP server name is required");
|
||||
if (!server) throw new Error("MCP server definition is required");
|
||||
if (!server.command) throw new Error("MCP server command is required");
|
||||
if (server.hasOwnProperty("args") && !Array.isArray(server.args))
|
||||
throw new Error("MCP server args must be an array");
|
||||
|
||||
this.log(`Attempting to start MCP server: ${name}`);
|
||||
const mcp = new Client({ name: name, version: "1.0.0" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: server.command,
|
||||
args: server?.args ?? [],
|
||||
});
|
||||
|
||||
// Add connection event listeners
|
||||
transport.onclose = () => this.log(`${name} - Transport closed`);
|
||||
transport.onerror = (error) =>
|
||||
this.log(`${name} - Transport error:`, error);
|
||||
transport.onmessage = (message) =>
|
||||
this.log(`${name} - Transport message:`, message);
|
||||
|
||||
// Connect and await the connection with a timeout
|
||||
this.mcps[name] = mcp;
|
||||
const connectionPromise = mcp.connect(transport);
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error("Connection timeout")), 30_000); // 30 second timeout
|
||||
});
|
||||
await Promise.race([connectionPromise, timeoutPromise]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot the MCP servers according to the server definitions.
|
||||
* This function will skip booting MCP servers if they are already running.
|
||||
* @returns { Promise<{ [key: string]: {status: string, message: string} }> } The results of the boot process.
|
||||
*/
|
||||
async bootMCPServers() {
|
||||
if (Object.keys(this.mcps).length > 0) {
|
||||
this.log("MCP Servers already running, skipping boot.");
|
||||
return this.mcpLoadingResults;
|
||||
}
|
||||
|
||||
const serverDefinitions = this.mcpServerConfigs;
|
||||
for (const { name, server } of serverDefinitions) {
|
||||
if (
|
||||
server.anythingllm?.hasOwnProperty("autoStart") &&
|
||||
server.anythingllm.autoStart === false
|
||||
) {
|
||||
this.log(
|
||||
`MCP server ${name} has anythingllm.autoStart property set to false, skipping boot!`
|
||||
);
|
||||
this.mcpLoadingResults[name] = {
|
||||
status: "failed",
|
||||
message: `MCP server ${name} has anythingllm.autoStart property set to false, boot skipped!`,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.#startMCPServer({ name, server });
|
||||
// Verify the connection is alive?
|
||||
// if (!(await mcp.ping())) throw new Error('Connection failed to establish');
|
||||
this.mcpLoadingResults[name] = {
|
||||
status: "success",
|
||||
message: `Successfully connected to MCP server: ${name}`,
|
||||
};
|
||||
} catch (e) {
|
||||
this.log(`Failed to start MCP server: ${name}`, {
|
||||
error: e.message,
|
||||
code: e.code,
|
||||
syscall: e.syscall,
|
||||
path: e.path,
|
||||
stack: e.stack, // Adding stack trace for better debugging
|
||||
});
|
||||
this.mcpLoadingResults[name] = {
|
||||
status: "failed",
|
||||
message: `Failed to start MCP server: ${name} [${e.code || "NO_CODE"}] ${e.message}`,
|
||||
};
|
||||
|
||||
// Clean up failed connection
|
||||
if (this.mcps[name]) {
|
||||
this.mcps[name].close();
|
||||
delete this.mcps[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const runningServers = Object.keys(this.mcps);
|
||||
this.log(
|
||||
`Successfully started ${runningServers.length} MCP servers:`,
|
||||
runningServers
|
||||
);
|
||||
return this.mcpLoadingResults;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MCPHypervisor;
|
||||
203
server/utils/MCP/index.js
Normal file
203
server/utils/MCP/index.js
Normal file
@ -0,0 +1,203 @@
|
||||
const MCPHypervisor = require("./hypervisor");
|
||||
|
||||
class MCPCompatibilityLayer extends MCPHypervisor {
|
||||
static _instance;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
if (MCPCompatibilityLayer._instance) return MCPCompatibilityLayer._instance;
|
||||
MCPCompatibilityLayer._instance = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the active MCP servers as plugins we can load into agents.
|
||||
* This will also boot all MCP servers if they have not been started yet.
|
||||
* @returns {Promise<string[]>} Array of flow names in @@mcp_{name} format
|
||||
*/
|
||||
async activeMCPServers() {
|
||||
await this.bootMCPServers();
|
||||
return Object.keys(this.mcps).flatMap((name) => `@@mcp_${name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an MCP server name to an AnythingLLM Agent plugin
|
||||
* @param {string} name - The base name of the MCP server to convert - not the tool name. eg: `docker-mcp` not `docker-mcp:list-containers`
|
||||
* @param {Object} aibitat - The aibitat object to pass to the plugin
|
||||
* @returns {Promise<{name: string, description: string, plugin: Function}[]|null>} Array of plugin configurations or null if not found
|
||||
*/
|
||||
async convertServerToolsToPlugins(name, _aibitat = null) {
|
||||
const mcp = this.mcps[name];
|
||||
if (!mcp) return null;
|
||||
|
||||
const tools = (await mcp.listTools()).tools;
|
||||
if (!tools.length) return null;
|
||||
|
||||
const plugins = [];
|
||||
for (const tool of tools) {
|
||||
plugins.push({
|
||||
name: `${name}-${tool.name}`,
|
||||
description: tool.description,
|
||||
plugin: function () {
|
||||
return {
|
||||
name: `${name}-${tool.name}`,
|
||||
setup: (aibitat) => {
|
||||
aibitat.function({
|
||||
super: aibitat,
|
||||
name: `${name}-${tool.name}`,
|
||||
controller: new AbortController(),
|
||||
description: tool.description,
|
||||
examples: [],
|
||||
parameters: {
|
||||
$schema: "http://json-schema.org/draft-07/schema#",
|
||||
...tool.inputSchema,
|
||||
},
|
||||
handler: async function (args = {}) {
|
||||
try {
|
||||
aibitat.handlerProps.log(
|
||||
`Executing MCP server: ${name}:${tool.name} with args:`,
|
||||
args
|
||||
);
|
||||
aibitat.introspect(
|
||||
`Executing MCP server: ${name} with ${JSON.stringify(args, null, 2)}`
|
||||
);
|
||||
const result = await mcp.callTool({
|
||||
name: tool.name,
|
||||
arguments: args,
|
||||
});
|
||||
aibitat.handlerProps.log(
|
||||
`MCP server: ${name}:${tool.name} completed successfully`,
|
||||
result
|
||||
);
|
||||
aibitat.introspect(
|
||||
`MCP server: ${name}:${tool.name} completed successfully`
|
||||
);
|
||||
return typeof result === "object"
|
||||
? JSON.stringify(result)
|
||||
: String(result);
|
||||
} catch (error) {
|
||||
aibitat.handlerProps.log(
|
||||
`MCP server: ${name}:${tool.name} failed with error:`,
|
||||
error
|
||||
);
|
||||
aibitat.introspect(
|
||||
`MCP server: ${name}:${tool.name} failed with error:`,
|
||||
error
|
||||
);
|
||||
return `The tool ${name}:${tool.name} failed with error: ${error?.message || "An unknown error occurred"}`;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
toolName: `${name}:${tool.name}`,
|
||||
});
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MCP servers that were loaded or attempted to be loaded
|
||||
* so that we can display them in the frontend for review or error logging.
|
||||
* @returns {Promise<{
|
||||
* name: string,
|
||||
* running: boolean,
|
||||
* tools: {name: string, description: string, inputSchema: Object}[],
|
||||
* process: {pid: number, cmd: string}|null,
|
||||
* error: string|null
|
||||
* }[]>} - The active MCP servers
|
||||
*/
|
||||
async servers() {
|
||||
await this.bootMCPServers();
|
||||
const servers = [];
|
||||
for (const [name, result] of Object.entries(this.mcpLoadingResults)) {
|
||||
const config = this.mcpServerConfigs.find((s) => s.name === name);
|
||||
|
||||
if (result.status === "failed") {
|
||||
servers.push({
|
||||
name,
|
||||
config: config?.server || null,
|
||||
running: false,
|
||||
tools: [],
|
||||
error: result.message,
|
||||
process: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const mcp = this.mcps[name];
|
||||
if (!mcp) {
|
||||
delete this.mcpLoadingResults[name];
|
||||
delete this.mcps[name];
|
||||
continue;
|
||||
}
|
||||
|
||||
const online = !!(await mcp.ping());
|
||||
const tools = online ? (await mcp.listTools()).tools : [];
|
||||
servers.push({
|
||||
name,
|
||||
config: config?.server || null,
|
||||
running: online,
|
||||
tools,
|
||||
error: null,
|
||||
process: {
|
||||
pid: mcp.transport._process.pid,
|
||||
},
|
||||
});
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the MCP server (start or stop)
|
||||
* @param {string} name - The name of the MCP server to toggle
|
||||
* @returns {Promise<{success: boolean, error: string | null}>}
|
||||
*/
|
||||
async toggleServerStatus(name) {
|
||||
const server = this.mcpServerConfigs.find((s) => s.name === name);
|
||||
if (!server)
|
||||
return {
|
||||
success: false,
|
||||
error: `MCP server ${name} not found in config file.`,
|
||||
};
|
||||
const mcp = this.mcps[name];
|
||||
const online = !!mcp ? !!(await mcp.ping()) : false; // If the server is not in the mcps object, it is not running
|
||||
|
||||
if (online) {
|
||||
const killed = this.pruneMCPServer(name);
|
||||
return {
|
||||
success: killed,
|
||||
error: killed ? null : `Failed to kill MCP server: ${name}`,
|
||||
};
|
||||
} else {
|
||||
const startupResult = await this.startMCPServer(name);
|
||||
return { success: startupResult.success, error: startupResult.error };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the MCP server - will also remove it from the config file
|
||||
* @param {string} name - The name of the MCP server to delete
|
||||
* @returns {Promise<{success: boolean, error: string | null}>}
|
||||
*/
|
||||
async deleteServer(name) {
|
||||
const server = this.mcpServerConfigs.find((s) => s.name === name);
|
||||
if (!server)
|
||||
return {
|
||||
success: false,
|
||||
error: `MCP server ${name} not found in config file.`,
|
||||
};
|
||||
|
||||
const mcp = this.mcps[name];
|
||||
const online = !!mcp ? !!(await mcp.ping()) : false; // If the server is not in the mcps object, it is not running
|
||||
if (online) this.pruneMCPServer(name);
|
||||
this.removeMCPServerFromConfig(name);
|
||||
|
||||
delete this.mcps[name];
|
||||
delete this.mcpLoadingResults[name];
|
||||
this.log(`MCP server was killed and removed from config file: ${name}`);
|
||||
return { success: true, error: null };
|
||||
}
|
||||
}
|
||||
module.exports = MCPCompatibilityLayer;
|
||||
@ -4,6 +4,7 @@ const { safeJsonParse } = require("../http");
|
||||
const Provider = require("./aibitat/providers/ai-provider");
|
||||
const ImportedPlugin = require("./imported");
|
||||
const { AgentFlows } = require("../agentFlows");
|
||||
const MCPCompatibilityLayer = require("../MCP");
|
||||
|
||||
// This is a list of skills that are built-in and default enabled.
|
||||
const DEFAULT_SKILLS = [
|
||||
@ -31,6 +32,7 @@ const WORKSPACE_AGENT = {
|
||||
...(await agentSkillsFromSystemSettings()),
|
||||
...ImportedPlugin.activeImportedPlugins(),
|
||||
...AgentFlows.activeFlowPlugins(),
|
||||
...(await new MCPCompatibilityLayer().activeMCPServers()),
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
@ -8,6 +8,7 @@ const { safeJsonParse } = require("../http");
|
||||
const { USER_AGENT, WORKSPACE_AGENT } = require("./defaults");
|
||||
const ImportedPlugin = require("./imported");
|
||||
const { AgentFlows } = require("../agentFlows");
|
||||
const MCPCompatibilityLayer = require("../MCP");
|
||||
|
||||
class AgentHandler {
|
||||
#invocationUUID;
|
||||
@ -411,6 +412,43 @@ class AgentHandler {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load MCP plugin. This is marked by `@@mcp_` in the array of functions to load.
|
||||
// All sub-tools are loaded here and are denoted by `pluginName:toolName` as their identifier.
|
||||
// This will replace the parent MCP server plugin with the sub-tools as child plugins so they
|
||||
// can be called directly by the agent when invoked.
|
||||
// Since to get to this point, the `activeMCPServers` method has already been called, we can
|
||||
// safely assume that the MCP server is running and the tools are available/loaded.
|
||||
if (name.startsWith("@@mcp_")) {
|
||||
const mcpPluginName = name.replace("@@mcp_", "");
|
||||
const plugins =
|
||||
await new MCPCompatibilityLayer().convertServerToolsToPlugins(
|
||||
mcpPluginName,
|
||||
this.aibitat
|
||||
);
|
||||
if (!plugins) {
|
||||
this.log(
|
||||
`MCP ${mcpPluginName} not found in MCP server config. Skipping inclusion to agent cluster.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the old function from the agent functions directly
|
||||
// and push the new ones onto the end of the array so that they are loaded properly.
|
||||
this.aibitat.agents.get("@agent").functions = this.aibitat.agents
|
||||
.get("@agent")
|
||||
.functions.filter((f) => f.name !== name);
|
||||
for (const plugin of plugins)
|
||||
this.aibitat.agents.get("@agent").functions.push(plugin.name);
|
||||
|
||||
plugins.forEach((plugin) => {
|
||||
this.aibitat.use(plugin.plugin());
|
||||
this.log(
|
||||
`Attached MCP::${plugin.toolName} MCP tool to Agent cluster`
|
||||
);
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load imported plugin. This is marked by `@@` in the array of functions to load.
|
||||
// and is the @@hubID of the plugin.
|
||||
if (name.startsWith("@@")) {
|
||||
|
||||
423
server/yarn.lock
423
server/yarn.lock
@ -1650,6 +1650,22 @@
|
||||
dependencies:
|
||||
ws "^7.5.10"
|
||||
|
||||
"@modelcontextprotocol/sdk@^1.8.0":
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.8.0.tgz#55cdd65054ec24e53800250c70e07429d669db67"
|
||||
integrity sha512-e06W7SwrontJDHwCawNO5SGxG+nU9AAx+jpHHZqGl/WrDBdWOpvirC+s58VpJTB5QemI4jTRcjWT4Pt3Q1NPQQ==
|
||||
dependencies:
|
||||
content-type "^1.0.5"
|
||||
cors "^2.8.5"
|
||||
cross-spawn "^7.0.3"
|
||||
eventsource "^3.0.2"
|
||||
express "^5.0.1"
|
||||
express-rate-limit "^7.5.0"
|
||||
pkce-challenge "^4.1.0"
|
||||
raw-body "^3.0.0"
|
||||
zod "^3.23.8"
|
||||
zod-to-json-schema "^3.24.1"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
|
||||
@ -2790,6 +2806,14 @@ abort-controller@^3.0.0:
|
||||
dependencies:
|
||||
event-target-shim "^5.0.0"
|
||||
|
||||
accepts@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895"
|
||||
integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==
|
||||
dependencies:
|
||||
mime-types "^3.0.0"
|
||||
negotiator "^1.0.0"
|
||||
|
||||
accepts@~1.3.8:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz"
|
||||
@ -3170,6 +3194,21 @@ body-parser@1.20.2, body-parser@^1.20.2:
|
||||
type-is "~1.6.18"
|
||||
unpipe "1.0.0"
|
||||
|
||||
body-parser@^2.0.1:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.1.0.tgz#2fd84396259e00fa75648835e2d95703bce8e890"
|
||||
integrity sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==
|
||||
dependencies:
|
||||
bytes "^3.1.2"
|
||||
content-type "^1.0.5"
|
||||
debug "^4.4.0"
|
||||
http-errors "^2.0.0"
|
||||
iconv-lite "^0.5.2"
|
||||
on-finished "^2.4.1"
|
||||
qs "^6.14.0"
|
||||
raw-body "^3.0.0"
|
||||
type-is "^2.0.0"
|
||||
|
||||
boolbase@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz"
|
||||
@ -3238,11 +3277,19 @@ busboy@^1.0.0:
|
||||
dependencies:
|
||||
streamsearch "^1.1.0"
|
||||
|
||||
bytes@3.1.2:
|
||||
bytes@3.1.2, bytes@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz"
|
||||
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
|
||||
|
||||
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
|
||||
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
function-bind "^1.1.2"
|
||||
|
||||
call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz"
|
||||
@ -3254,6 +3301,14 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
|
||||
get-intrinsic "^1.2.4"
|
||||
set-function-length "^1.2.1"
|
||||
|
||||
call-bound@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a"
|
||||
integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.2"
|
||||
get-intrinsic "^1.3.0"
|
||||
|
||||
callsites@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
|
||||
@ -3572,7 +3627,14 @@ content-disposition@0.5.4:
|
||||
dependencies:
|
||||
safe-buffer "5.2.1"
|
||||
|
||||
content-type@~1.0.4, content-type@~1.0.5:
|
||||
content-disposition@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.0.tgz#844426cb398f934caefcbb172200126bc7ceace2"
|
||||
integrity sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==
|
||||
dependencies:
|
||||
safe-buffer "5.2.1"
|
||||
|
||||
content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz"
|
||||
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
|
||||
@ -3582,11 +3644,21 @@ cookie-signature@1.0.6:
|
||||
resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz"
|
||||
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
|
||||
|
||||
cookie-signature@^1.2.1:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793"
|
||||
integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==
|
||||
|
||||
cookie@0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz"
|
||||
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
|
||||
|
||||
cookie@0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9"
|
||||
integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
|
||||
@ -3711,6 +3783,13 @@ debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@4.3.6:
|
||||
version "4.3.6"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
|
||||
integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^3.2.7:
|
||||
version "3.2.7"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
|
||||
@ -3718,6 +3797,13 @@ debug@^3.2.7:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.3.5, debug@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
|
||||
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
||||
dependencies:
|
||||
ms "^2.1.3"
|
||||
|
||||
decamelize@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz"
|
||||
@ -3788,7 +3874,7 @@ depd@2.0.0:
|
||||
resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
|
||||
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
||||
|
||||
destroy@1.2.0:
|
||||
destroy@1.2.0, destroy@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz"
|
||||
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
|
||||
@ -3864,6 +3950,15 @@ dotenv@^16.0.3:
|
||||
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz"
|
||||
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
|
||||
|
||||
dunder-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
|
||||
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
gopd "^1.2.0"
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz"
|
||||
@ -3903,6 +3998,11 @@ encode32@^1.1.0:
|
||||
resolved "https://registry.npmjs.org/encode32/-/encode32-1.1.0.tgz"
|
||||
integrity sha512-BCmijZ4lWec5+fuGHclf7HSZf+mos2ncQkBjtvomvRWVEGAMI/tw56fuN2x4e+FTgQuTPbZjODPwX80lFy958w==
|
||||
|
||||
encodeurl@^2.0.0, encodeurl@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
|
||||
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
|
||||
|
||||
encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"
|
||||
@ -4008,6 +4108,11 @@ es-define-property@^1.0.0:
|
||||
dependencies:
|
||||
get-intrinsic "^1.2.4"
|
||||
|
||||
es-define-property@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
|
||||
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
|
||||
|
||||
es-errors@^1.1.0, es-errors@^1.2.1, es-errors@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz"
|
||||
@ -4040,6 +4145,13 @@ es-object-atoms@^1.0.0:
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
|
||||
es-object-atoms@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
|
||||
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
|
||||
es-set-tostringtag@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz"
|
||||
@ -4070,7 +4182,7 @@ escalade@^3.1.1:
|
||||
resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz"
|
||||
integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
|
||||
|
||||
escape-html@~1.0.3:
|
||||
escape-html@^1.0.3, escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz"
|
||||
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
|
||||
@ -4225,7 +4337,7 @@ esutils@^2.0.2:
|
||||
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
|
||||
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
||||
|
||||
etag@~1.8.1:
|
||||
etag@^1.8.1, etag@~1.8.1:
|
||||
version "1.8.1"
|
||||
resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz"
|
||||
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
|
||||
@ -4245,6 +4357,18 @@ events@^3.0.0, events@^3.3.0:
|
||||
resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
|
||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
||||
|
||||
eventsource-parser@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.0.tgz#9303e303ef807d279ee210a17ce80f16300d9f57"
|
||||
integrity sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==
|
||||
|
||||
eventsource@^3.0.2:
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-3.0.5.tgz#0cae1eee2d2c75894de8b02a91d84e5c57f0cc5a"
|
||||
integrity sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==
|
||||
dependencies:
|
||||
eventsource-parser "^3.0.0"
|
||||
|
||||
execa@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz"
|
||||
@ -4270,6 +4394,11 @@ expr-eval@^2.0.2:
|
||||
resolved "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz"
|
||||
integrity sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==
|
||||
|
||||
express-rate-limit@^7.5.0:
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.5.0.tgz#6a67990a724b4fbbc69119419feef50c51e8b28f"
|
||||
integrity sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==
|
||||
|
||||
express@^4.18.2:
|
||||
version "4.19.2"
|
||||
resolved "https://registry.npmjs.org/express/-/express-4.19.2.tgz"
|
||||
@ -4307,6 +4436,44 @@ express@^4.18.2:
|
||||
utils-merge "1.0.1"
|
||||
vary "~1.1.2"
|
||||
|
||||
express@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-5.0.1.tgz#5d359a2550655be33124ecbc7400cd38436457e9"
|
||||
integrity sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==
|
||||
dependencies:
|
||||
accepts "^2.0.0"
|
||||
body-parser "^2.0.1"
|
||||
content-disposition "^1.0.0"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.7.1"
|
||||
cookie-signature "^1.2.1"
|
||||
debug "4.3.6"
|
||||
depd "2.0.0"
|
||||
encodeurl "~2.0.0"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
finalhandler "^2.0.0"
|
||||
fresh "2.0.0"
|
||||
http-errors "2.0.0"
|
||||
merge-descriptors "^2.0.0"
|
||||
methods "~1.1.2"
|
||||
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.13.0"
|
||||
range-parser "~1.2.1"
|
||||
router "^2.0.0"
|
||||
safe-buffer "5.2.1"
|
||||
send "^1.1.0"
|
||||
serve-static "^2.1.0"
|
||||
setprototypeof "1.2.0"
|
||||
statuses "2.0.1"
|
||||
type-is "^2.0.0"
|
||||
utils-merge "1.0.1"
|
||||
vary "~1.1.2"
|
||||
|
||||
external-editor@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz"
|
||||
@ -4411,6 +4578,18 @@ finalhandler@1.2.0:
|
||||
statuses "2.0.1"
|
||||
unpipe "~1.0.0"
|
||||
|
||||
finalhandler@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.0.tgz#72306373aa89d05a8242ed569ed86a1bff7c561f"
|
||||
integrity sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==
|
||||
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"
|
||||
|
||||
find-replace@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38"
|
||||
@ -4532,11 +4711,16 @@ forwarded@0.2.0:
|
||||
resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz"
|
||||
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
||||
|
||||
fresh@0.5.2:
|
||||
fresh@0.5.2, fresh@^0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
|
||||
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
|
||||
|
||||
fresh@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4"
|
||||
integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==
|
||||
|
||||
fs-constants@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz"
|
||||
@ -4622,6 +4806,30 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@
|
||||
has-symbols "^1.0.3"
|
||||
hasown "^2.0.0"
|
||||
|
||||
get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
|
||||
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
|
||||
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:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
|
||||
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
|
||||
dependencies:
|
||||
dunder-proto "^1.0.1"
|
||||
es-object-atoms "^1.0.0"
|
||||
|
||||
get-stream@^6.0.0:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz"
|
||||
@ -4689,6 +4897,11 @@ gopd@^1.0.1:
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
|
||||
gopd@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
|
||||
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
|
||||
|
||||
graphemer@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz"
|
||||
@ -4746,6 +4959,11 @@ has-symbols@^1.0.2, has-symbols@^1.0.3:
|
||||
resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz"
|
||||
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
|
||||
|
||||
has-symbols@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
|
||||
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
|
||||
|
||||
has-tostringtag@^1.0.0, has-tostringtag@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz"
|
||||
@ -4813,7 +5031,7 @@ htmlparser2@^9.1.0:
|
||||
domutils "^3.1.0"
|
||||
entities "^4.5.0"
|
||||
|
||||
http-errors@2.0.0:
|
||||
http-errors@2.0.0, http-errors@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz"
|
||||
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
|
||||
@ -4881,6 +5099,13 @@ iconv-lite@0.6.3, iconv-lite@^0.6.2, iconv-lite@^0.6.3:
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||
|
||||
iconv-lite@^0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.2.tgz#af6d628dccfb463b7364d97f715e4b74b8c8c2b8"
|
||||
integrity sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
ieee754@^1.1.13, ieee754@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
|
||||
@ -5101,6 +5326,11 @@ is-path-inside@^3.0.3:
|
||||
resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz"
|
||||
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||
|
||||
is-promise@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3"
|
||||
integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==
|
||||
|
||||
is-property@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz"
|
||||
@ -5635,6 +5865,11 @@ make-dir@^3.1.0:
|
||||
dependencies:
|
||||
semver "^6.0.0"
|
||||
|
||||
math-intrinsics@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
|
||||
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
|
||||
|
||||
md5@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz"
|
||||
@ -5649,11 +5884,21 @@ media-typer@0.3.0:
|
||||
resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
|
||||
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
|
||||
|
||||
media-typer@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561"
|
||||
integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz"
|
||||
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
||||
|
||||
merge-descriptors@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808"
|
||||
integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz"
|
||||
@ -5669,13 +5914,25 @@ mime-db@1.52.0:
|
||||
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
|
||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||
|
||||
mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
|
||||
mime-db@^1.54.0:
|
||||
version "1.54.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5"
|
||||
integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==
|
||||
|
||||
mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.24, mime-types@~2.1.34:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
dependencies:
|
||||
mime-db "1.52.0"
|
||||
|
||||
mime-types@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce"
|
||||
integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==
|
||||
dependencies:
|
||||
mime-db "^1.54.0"
|
||||
|
||||
mime@1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz"
|
||||
@ -5883,6 +6140,11 @@ negotiator@0.6.3:
|
||||
resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz"
|
||||
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
|
||||
|
||||
negotiator@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a"
|
||||
integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==
|
||||
|
||||
node-abi@^3.3.0:
|
||||
version "3.62.0"
|
||||
resolved "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz"
|
||||
@ -6016,6 +6278,11 @@ object-inspect@^1.13.1:
|
||||
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz"
|
||||
integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
|
||||
|
||||
object-inspect@^1.13.3:
|
||||
version "1.13.4"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213"
|
||||
integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==
|
||||
|
||||
object-keys@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
|
||||
@ -6075,14 +6342,14 @@ ollama@^0.5.0, ollama@^0.5.10:
|
||||
dependencies:
|
||||
whatwg-fetch "^3.6.20"
|
||||
|
||||
on-finished@2.4.1:
|
||||
on-finished@2.4.1, on-finished@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz"
|
||||
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
|
||||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
once@1.4.0, once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
|
||||
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||
@ -6276,7 +6543,7 @@ parse5@^7.0.0, parse5@^7.1.2:
|
||||
dependencies:
|
||||
entities "^4.5.0"
|
||||
|
||||
parseurl@~1.3.3:
|
||||
parseurl@^1.3.3, parseurl@~1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"
|
||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
||||
@ -6306,6 +6573,11 @@ path-to-regexp@0.1.7:
|
||||
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz"
|
||||
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
|
||||
|
||||
path-to-regexp@^8.0.0:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4"
|
||||
integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==
|
||||
|
||||
pg-cloudflare@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz"
|
||||
@ -6381,6 +6653,11 @@ pirates@^3.0.2:
|
||||
dependencies:
|
||||
node-modules-regexp "^1.0.0"
|
||||
|
||||
pkce-challenge@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-4.1.0.tgz#95027d7750c3c0f21676a345b48f481786f9acdb"
|
||||
integrity sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==
|
||||
|
||||
platform@^1.3.6:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz"
|
||||
@ -6574,6 +6851,20 @@ qs@6.11.2:
|
||||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
qs@6.13.0:
|
||||
version "6.13.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
|
||||
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
|
||||
dependencies:
|
||||
side-channel "^1.0.6"
|
||||
|
||||
qs@^6.14.0:
|
||||
version "6.14.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930"
|
||||
integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==
|
||||
dependencies:
|
||||
side-channel "^1.1.0"
|
||||
|
||||
queue-microtask@^1.2.2:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
|
||||
@ -6584,7 +6875,7 @@ queue-tick@^1.0.1:
|
||||
resolved "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz"
|
||||
integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==
|
||||
|
||||
range-parser@~1.2.1:
|
||||
range-parser@^1.2.1, range-parser@~1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz"
|
||||
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
||||
@ -6599,6 +6890,16 @@ raw-body@2.5.2:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
raw-body@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f"
|
||||
integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
http-errors "2.0.0"
|
||||
iconv-lite "0.6.3"
|
||||
unpipe "1.0.0"
|
||||
|
||||
rc@^1.2.7:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz"
|
||||
@ -6744,6 +7045,15 @@ rimraf@^3.0.2:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
router@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/router/-/router-2.1.0.tgz#f256ca2365afb4d386ba4f7a9ee0aa0827c962fa"
|
||||
integrity sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==
|
||||
dependencies:
|
||||
is-promise "^4.0.0"
|
||||
parseurl "^1.3.3"
|
||||
path-to-regexp "^8.0.0"
|
||||
|
||||
run-parallel@^1.1.9:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz"
|
||||
@ -6841,6 +7151,24 @@ send@0.18.0:
|
||||
range-parser "~1.2.1"
|
||||
statuses "2.0.1"
|
||||
|
||||
send@^1.0.0, send@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-1.1.0.tgz#4efe6ff3bb2139b0e5b2648d8b18d4dec48fc9c5"
|
||||
integrity sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==
|
||||
dependencies:
|
||||
debug "^4.3.5"
|
||||
destroy "^1.2.0"
|
||||
encodeurl "^2.0.0"
|
||||
escape-html "^1.0.3"
|
||||
etag "^1.8.1"
|
||||
fresh "^0.5.2"
|
||||
http-errors "^2.0.0"
|
||||
mime-types "^2.1.35"
|
||||
ms "^2.1.3"
|
||||
on-finished "^2.4.1"
|
||||
range-parser "^1.2.1"
|
||||
statuses "^2.0.1"
|
||||
|
||||
seq-queue@^0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz"
|
||||
@ -6856,6 +7184,16 @@ serve-static@1.15.0:
|
||||
parseurl "~1.3.3"
|
||||
send "0.18.0"
|
||||
|
||||
serve-static@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.1.0.tgz#1b4eacbe93006b79054faa4d6d0a501d7f0e84e2"
|
||||
integrity sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==
|
||||
dependencies:
|
||||
encodeurl "^2.0.0"
|
||||
escape-html "^1.0.3"
|
||||
parseurl "^1.3.3"
|
||||
send "^1.0.0"
|
||||
|
||||
set-blocking@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz"
|
||||
@ -6914,6 +7252,35 @@ shebang-regex@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz"
|
||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||
|
||||
side-channel-list@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad"
|
||||
integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
object-inspect "^1.13.3"
|
||||
|
||||
side-channel-map@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42"
|
||||
integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
|
||||
dependencies:
|
||||
call-bound "^1.0.2"
|
||||
es-errors "^1.3.0"
|
||||
get-intrinsic "^1.2.5"
|
||||
object-inspect "^1.13.3"
|
||||
|
||||
side-channel-weakmap@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea"
|
||||
integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
|
||||
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"
|
||||
|
||||
side-channel@^1.0.4, side-channel@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz"
|
||||
@ -6924,6 +7291,17 @@ side-channel@^1.0.4, side-channel@^1.0.6:
|
||||
get-intrinsic "^1.2.4"
|
||||
object-inspect "^1.13.1"
|
||||
|
||||
side-channel@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9"
|
||||
integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
|
||||
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"
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.3:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz"
|
||||
@ -6987,7 +7365,7 @@ stack-trace@0.0.x:
|
||||
resolved "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz"
|
||||
integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==
|
||||
|
||||
statuses@2.0.1:
|
||||
statuses@2.0.1, statuses@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
|
||||
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
|
||||
@ -7361,6 +7739,15 @@ type-is@^1.6.4, type-is@~1.6.18:
|
||||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
type-is@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.0.tgz#7d249c2e2af716665cc149575dadb8b3858653af"
|
||||
integrity sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==
|
||||
dependencies:
|
||||
content-type "^1.0.5"
|
||||
media-typer "^1.1.0"
|
||||
mime-types "^3.0.0"
|
||||
|
||||
typed-array-buffer@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz"
|
||||
@ -7780,7 +8167,17 @@ zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.22.4, zod-to-json-schema@^3.22
|
||||
resolved "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.0.tgz"
|
||||
integrity sha512-az0uJ243PxsRIa2x1WmNE/pnuA05gUq/JB8Lwe1EDCCL/Fz9MgjYQ0fPlyc2Tcv6aF2ZA7WM5TWaRZVEFaAIag==
|
||||
|
||||
zod-to-json-schema@^3.24.1:
|
||||
version "3.24.5"
|
||||
resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz#d1095440b147fb7c2093812a53c54df8d5df50a3"
|
||||
integrity sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==
|
||||
|
||||
zod@^3.22.3, zod@^3.22.4:
|
||||
version "3.23.5"
|
||||
resolved "https://registry.npmjs.org/zod/-/zod-3.23.5.tgz"
|
||||
integrity sha512-fkwiq0VIQTksNNA131rDOsVJcns0pfVUjHzLrNBiF/O/Xxb5lQyEXkhZWcJ7npWsYlvs+h0jFWXXy4X46Em1JA==
|
||||
|
||||
zod@^3.23.8:
|
||||
version "3.24.2"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3"
|
||||
integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==
|
||||
|
||||
Loading…
Reference in New Issue
Block a user