Merge branch 'main' into claude/issue-1028-20260121-1235
This commit is contained in:
commit
edbe2f84f3
13 changed files with 1479 additions and 108 deletions
|
|
@ -16,6 +16,8 @@ import {
|
||||||
ServerNotification,
|
ServerNotification,
|
||||||
Tool,
|
Tool,
|
||||||
LoggingLevel,
|
LoggingLevel,
|
||||||
|
Task,
|
||||||
|
GetTaskResultSchema,
|
||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { OAuthTokensSchema } from "@modelcontextprotocol/sdk/shared/auth.js";
|
import { OAuthTokensSchema } from "@modelcontextprotocol/sdk/shared/auth.js";
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -55,6 +57,7 @@ import {
|
||||||
Hammer,
|
Hammer,
|
||||||
Hash,
|
Hash,
|
||||||
Key,
|
Key,
|
||||||
|
ListTodo,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
Settings,
|
Settings,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
@ -71,6 +74,7 @@ import RootsTab from "./components/RootsTab";
|
||||||
import SamplingTab, { PendingRequest } from "./components/SamplingTab";
|
import SamplingTab, { PendingRequest } from "./components/SamplingTab";
|
||||||
import Sidebar from "./components/Sidebar";
|
import Sidebar from "./components/Sidebar";
|
||||||
import ToolsTab from "./components/ToolsTab";
|
import ToolsTab from "./components/ToolsTab";
|
||||||
|
import TasksTab from "./components/TasksTab";
|
||||||
import { InspectorConfig } from "./lib/configurationTypes";
|
import { InspectorConfig } from "./lib/configurationTypes";
|
||||||
import {
|
import {
|
||||||
getMCPProxyAddress,
|
getMCPProxyAddress,
|
||||||
|
|
@ -81,6 +85,7 @@ import {
|
||||||
getInitialArgs,
|
getInitialArgs,
|
||||||
initializeInspectorConfig,
|
initializeInspectorConfig,
|
||||||
saveInspectorConfig,
|
saveInspectorConfig,
|
||||||
|
getMCPTaskTtl,
|
||||||
} from "./utils/configUtils";
|
} from "./utils/configUtils";
|
||||||
import ElicitationTab, {
|
import ElicitationTab, {
|
||||||
PendingElicitationRequest,
|
PendingElicitationRequest,
|
||||||
|
|
@ -124,12 +129,14 @@ const App = () => {
|
||||||
const [prompts, setPrompts] = useState<Prompt[]>([]);
|
const [prompts, setPrompts] = useState<Prompt[]>([]);
|
||||||
const [promptContent, setPromptContent] = useState<string>("");
|
const [promptContent, setPromptContent] = useState<string>("");
|
||||||
const [tools, setTools] = useState<Tool[]>([]);
|
const [tools, setTools] = useState<Tool[]>([]);
|
||||||
|
const [tasks, setTasks] = useState<Task[]>([]);
|
||||||
const [toolResult, setToolResult] =
|
const [toolResult, setToolResult] =
|
||||||
useState<CompatibilityCallToolResult | null>(null);
|
useState<CompatibilityCallToolResult | null>(null);
|
||||||
const [errors, setErrors] = useState<Record<string, string | null>>({
|
const [errors, setErrors] = useState<Record<string, string | null>>({
|
||||||
resources: null,
|
resources: null,
|
||||||
prompts: null,
|
prompts: null,
|
||||||
tools: null,
|
tools: null,
|
||||||
|
tasks: null,
|
||||||
});
|
});
|
||||||
const [command, setCommand] = useState<string>(getInitialCommand);
|
const [command, setCommand] = useState<string>(getInitialCommand);
|
||||||
const [args, setArgs] = useState<string>(getInitialArgs);
|
const [args, setArgs] = useState<string>(getInitialArgs);
|
||||||
|
|
@ -265,6 +272,8 @@ const App = () => {
|
||||||
|
|
||||||
const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
|
const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
|
||||||
const [selectedTool, setSelectedTool] = useState<Tool | null>(null);
|
const [selectedTool, setSelectedTool] = useState<Tool | null>(null);
|
||||||
|
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
|
||||||
|
const [isPollingTask, setIsPollingTask] = useState(false);
|
||||||
const [nextResourceCursor, setNextResourceCursor] = useState<
|
const [nextResourceCursor, setNextResourceCursor] = useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
|
|
@ -275,6 +284,7 @@ const App = () => {
|
||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
const [nextToolCursor, setNextToolCursor] = useState<string | undefined>();
|
const [nextToolCursor, setNextToolCursor] = useState<string | undefined>();
|
||||||
|
const [nextTaskCursor, setNextTaskCursor] = useState<string | undefined>();
|
||||||
const progressTokenRef = useRef(0);
|
const progressTokenRef = useRef(0);
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<string>(() => {
|
const [activeTab, setActiveTab] = useState<string>(() => {
|
||||||
|
|
@ -290,6 +300,32 @@ const App = () => {
|
||||||
currentTabRef.current = activeTab;
|
currentTabRef.current = activeTab;
|
||||||
}, [activeTab]);
|
}, [activeTab]);
|
||||||
|
|
||||||
|
const navigateToOriginatingTab = (originatingTab?: string) => {
|
||||||
|
if (!originatingTab) return;
|
||||||
|
|
||||||
|
const validTabs = [
|
||||||
|
...(serverCapabilities?.resources ? ["resources"] : []),
|
||||||
|
...(serverCapabilities?.prompts ? ["prompts"] : []),
|
||||||
|
...(serverCapabilities?.tools ? ["tools"] : []),
|
||||||
|
...(serverCapabilities?.tasks ? ["tasks"] : []),
|
||||||
|
"ping",
|
||||||
|
"sampling",
|
||||||
|
"elicitations",
|
||||||
|
"roots",
|
||||||
|
"auth",
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!validTabs.includes(originatingTab)) return;
|
||||||
|
|
||||||
|
setActiveTab(originatingTab);
|
||||||
|
window.location.hash = originatingTab;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setActiveTab(originatingTab);
|
||||||
|
window.location.hash = originatingTab;
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
const { height: historyPaneHeight, handleDragStart } = useDraggablePane(300);
|
const { height: historyPaneHeight, handleDragStart } = useDraggablePane(300);
|
||||||
const {
|
const {
|
||||||
width: sidebarWidth,
|
width: sidebarWidth,
|
||||||
|
|
@ -297,6 +333,11 @@ const App = () => {
|
||||||
handleDragStart: handleSidebarDragStart,
|
handleDragStart: handleSidebarDragStart,
|
||||||
} = useDraggableSidebar(320);
|
} = useDraggableSidebar(320);
|
||||||
|
|
||||||
|
const selectedTaskRef = useRef<Task | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
selectedTaskRef.current = selectedTask;
|
||||||
|
}, [selectedTask]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
connectionStatus,
|
connectionStatus,
|
||||||
serverCapabilities,
|
serverCapabilities,
|
||||||
|
|
@ -305,6 +346,8 @@ const App = () => {
|
||||||
requestHistory,
|
requestHistory,
|
||||||
clearRequestHistory,
|
clearRequestHistory,
|
||||||
makeRequest,
|
makeRequest,
|
||||||
|
cancelTask: cancelMcpTask,
|
||||||
|
listTasks: listMcpTasks,
|
||||||
sendNotification,
|
sendNotification,
|
||||||
handleCompletion,
|
handleCompletion,
|
||||||
completionsSupported,
|
completionsSupported,
|
||||||
|
|
@ -324,12 +367,41 @@ const App = () => {
|
||||||
connectionType,
|
connectionType,
|
||||||
onNotification: (notification) => {
|
onNotification: (notification) => {
|
||||||
setNotifications((prev) => [...prev, notification as ServerNotification]);
|
setNotifications((prev) => [...prev, notification as ServerNotification]);
|
||||||
|
|
||||||
|
if (notification.method === "notifications/tasks/list_changed") {
|
||||||
|
void listTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification.method === "notifications/tasks/status") {
|
||||||
|
const task = notification.params as unknown as Task;
|
||||||
|
setTasks((prev) => {
|
||||||
|
const exists = prev.some((t) => t.taskId === task.taskId);
|
||||||
|
if (exists) {
|
||||||
|
return prev.map((t) => (t.taskId === task.taskId ? task : t));
|
||||||
|
} else {
|
||||||
|
return [task, ...prev];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (selectedTaskRef.current?.taskId === task.taskId) {
|
||||||
|
setSelectedTask(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onPendingRequest: (request, resolve, reject) => {
|
onPendingRequest: (request, resolve, reject) => {
|
||||||
|
const currentTab = lastToolCallOriginTabRef.current;
|
||||||
setPendingSampleRequests((prev) => [
|
setPendingSampleRequests((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
{ id: nextRequestId.current++, request, resolve, reject },
|
{
|
||||||
|
id: nextRequestId.current++,
|
||||||
|
request,
|
||||||
|
originatingTab: currentTab,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
setActiveTab("sampling");
|
||||||
|
window.location.hash = "sampling";
|
||||||
},
|
},
|
||||||
onElicitationRequest: (request, resolve) => {
|
onElicitationRequest: (request, resolve) => {
|
||||||
const currentTab = lastToolCallOriginTabRef.current;
|
const currentTab = lastToolCallOriginTabRef.current;
|
||||||
|
|
@ -367,6 +439,7 @@ const App = () => {
|
||||||
...(serverCapabilities?.resources ? ["resources"] : []),
|
...(serverCapabilities?.resources ? ["resources"] : []),
|
||||||
...(serverCapabilities?.prompts ? ["prompts"] : []),
|
...(serverCapabilities?.prompts ? ["prompts"] : []),
|
||||||
...(serverCapabilities?.tools ? ["tools"] : []),
|
...(serverCapabilities?.tools ? ["tools"] : []),
|
||||||
|
...(serverCapabilities?.tasks ? ["tasks"] : []),
|
||||||
"ping",
|
"ping",
|
||||||
"sampling",
|
"sampling",
|
||||||
"elicitations",
|
"elicitations",
|
||||||
|
|
@ -383,7 +456,9 @@ const App = () => {
|
||||||
? "prompts"
|
? "prompts"
|
||||||
: serverCapabilities?.tools
|
: serverCapabilities?.tools
|
||||||
? "tools"
|
? "tools"
|
||||||
: "ping";
|
: serverCapabilities?.tasks
|
||||||
|
? "tasks"
|
||||||
|
: "ping";
|
||||||
|
|
||||||
setActiveTab(defaultTab);
|
setActiveTab(defaultTab);
|
||||||
window.location.hash = defaultTab;
|
window.location.hash = defaultTab;
|
||||||
|
|
@ -391,6 +466,13 @@ const App = () => {
|
||||||
}
|
}
|
||||||
}, [serverCapabilities]);
|
}, [serverCapabilities]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (mcpClient && activeTab === "tasks") {
|
||||||
|
void listTasks();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [mcpClient, activeTab]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem("lastCommand", command);
|
localStorage.setItem("lastCommand", command);
|
||||||
}, [command]);
|
}, [command]);
|
||||||
|
|
@ -610,7 +692,9 @@ const App = () => {
|
||||||
? "prompts"
|
? "prompts"
|
||||||
: serverCapabilities?.tools
|
: serverCapabilities?.tools
|
||||||
? "tools"
|
? "tools"
|
||||||
: "ping";
|
: serverCapabilities?.tasks
|
||||||
|
? "tasks"
|
||||||
|
: "ping";
|
||||||
window.location.hash = defaultTab;
|
window.location.hash = defaultTab;
|
||||||
} else if (!mcpClient && window.location.hash) {
|
} else if (!mcpClient && window.location.hash) {
|
||||||
// Clear hash when disconnected - completely remove the fragment
|
// Clear hash when disconnected - completely remove the fragment
|
||||||
|
|
@ -638,6 +722,9 @@ const App = () => {
|
||||||
setPendingSampleRequests((prev) => {
|
setPendingSampleRequests((prev) => {
|
||||||
const request = prev.find((r) => r.id === id);
|
const request = prev.find((r) => r.id === id);
|
||||||
request?.resolve(result);
|
request?.resolve(result);
|
||||||
|
|
||||||
|
navigateToOriginatingTab(request?.originatingTab);
|
||||||
|
|
||||||
return prev.filter((r) => r.id !== id);
|
return prev.filter((r) => r.id !== id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -646,6 +733,9 @@ const App = () => {
|
||||||
setPendingSampleRequests((prev) => {
|
setPendingSampleRequests((prev) => {
|
||||||
const request = prev.find((r) => r.id === id);
|
const request = prev.find((r) => r.id === id);
|
||||||
request?.reject(new Error("Sampling request rejected"));
|
request?.reject(new Error("Sampling request rejected"));
|
||||||
|
|
||||||
|
navigateToOriginatingTab(request?.originatingTab);
|
||||||
|
|
||||||
return prev.filter((r) => r.id !== id);
|
return prev.filter((r) => r.id !== id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -666,6 +756,7 @@ const App = () => {
|
||||||
...(serverCapabilities?.resources ? ["resources"] : []),
|
...(serverCapabilities?.resources ? ["resources"] : []),
|
||||||
...(serverCapabilities?.prompts ? ["prompts"] : []),
|
...(serverCapabilities?.prompts ? ["prompts"] : []),
|
||||||
...(serverCapabilities?.tools ? ["tools"] : []),
|
...(serverCapabilities?.tools ? ["tools"] : []),
|
||||||
|
...(serverCapabilities?.tasks ? ["tasks"] : []),
|
||||||
"ping",
|
"ping",
|
||||||
"sampling",
|
"sampling",
|
||||||
"elicitations",
|
"elicitations",
|
||||||
|
|
@ -841,6 +932,7 @@ const App = () => {
|
||||||
name: string,
|
name: string,
|
||||||
params: Record<string, unknown>,
|
params: Record<string, unknown>,
|
||||||
toolMetadata?: Record<string, unknown>,
|
toolMetadata?: Record<string, unknown>,
|
||||||
|
runAsTask?: boolean,
|
||||||
) => {
|
) => {
|
||||||
lastToolCallOriginTabRef.current = currentTabRef.current;
|
lastToolCallOriginTabRef.current = currentTabRef.current;
|
||||||
|
|
||||||
|
|
@ -859,20 +951,164 @@ const App = () => {
|
||||||
...toolMetadata, // Tool-specific metadata
|
...toolMetadata, // Tool-specific metadata
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await sendMCPRequest(
|
const request: ClientRequest = {
|
||||||
{
|
method: "tools/call" as const,
|
||||||
method: "tools/call" as const,
|
params: {
|
||||||
params: {
|
name,
|
||||||
name,
|
arguments: cleanedParams,
|
||||||
arguments: cleanedParams,
|
_meta: mergedMetadata,
|
||||||
_meta: mergedMetadata,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (runAsTask) {
|
||||||
|
request.params = {
|
||||||
|
...request.params,
|
||||||
|
task: {
|
||||||
|
ttl: getMCPTaskTtl(config),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await sendMCPRequest(
|
||||||
|
request,
|
||||||
CompatibilityCallToolResultSchema,
|
CompatibilityCallToolResultSchema,
|
||||||
"tools",
|
"tools",
|
||||||
);
|
);
|
||||||
|
|
||||||
setToolResult(response);
|
// Check if this was a task-augmented request that returned a task reference
|
||||||
|
// The server returns { task: { taskId, status, ... } } when a task is created
|
||||||
|
const isTaskResult = (
|
||||||
|
res: unknown,
|
||||||
|
): res is {
|
||||||
|
task: { taskId: string; status: string; pollInterval: number };
|
||||||
|
} =>
|
||||||
|
!!res &&
|
||||||
|
typeof res === "object" &&
|
||||||
|
"task" in res &&
|
||||||
|
!!res.task &&
|
||||||
|
typeof res.task === "object" &&
|
||||||
|
"taskId" in res.task;
|
||||||
|
|
||||||
|
if (runAsTask && isTaskResult(response)) {
|
||||||
|
const taskId = response.task.taskId;
|
||||||
|
const pollInterval = response.task.pollInterval;
|
||||||
|
// Set polling state BEFORE setting tool result for proper UI update
|
||||||
|
setIsPollingTask(true);
|
||||||
|
// Safely extract any _meta from the original response (if present)
|
||||||
|
const initialResponseMeta =
|
||||||
|
response &&
|
||||||
|
typeof response === "object" &&
|
||||||
|
"_meta" in (response as Record<string, unknown>)
|
||||||
|
? ((response as { _meta?: Record<string, unknown> })._meta ?? {})
|
||||||
|
: undefined;
|
||||||
|
setToolResult({
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Task created: ${taskId}. Polling for status...`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
_meta: {
|
||||||
|
...(initialResponseMeta || {}),
|
||||||
|
"io.modelcontextprotocol/related-task": { taskId },
|
||||||
|
},
|
||||||
|
} as CompatibilityCallToolResult);
|
||||||
|
|
||||||
|
// Polling loop
|
||||||
|
let taskCompleted = false;
|
||||||
|
while (!taskCompleted) {
|
||||||
|
try {
|
||||||
|
// Wait for 1 second before polling
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
||||||
|
|
||||||
|
const taskStatus = await sendMCPRequest(
|
||||||
|
{
|
||||||
|
method: "tasks/get",
|
||||||
|
params: { taskId },
|
||||||
|
},
|
||||||
|
GetTaskResultSchema,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
taskStatus.status === "completed" ||
|
||||||
|
taskStatus.status === "failed" ||
|
||||||
|
taskStatus.status === "cancelled"
|
||||||
|
) {
|
||||||
|
taskCompleted = true;
|
||||||
|
console.log(
|
||||||
|
`Polling complete for task ${taskId}: ${taskStatus.status}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (taskStatus.status === "completed") {
|
||||||
|
console.log(`Fetching result for task ${taskId}`);
|
||||||
|
const result = await sendMCPRequest(
|
||||||
|
{
|
||||||
|
method: "tasks/result",
|
||||||
|
params: { taskId },
|
||||||
|
},
|
||||||
|
CompatibilityCallToolResultSchema,
|
||||||
|
);
|
||||||
|
console.log(`Result received for task ${taskId}:`, result);
|
||||||
|
setToolResult(result as CompatibilityCallToolResult);
|
||||||
|
|
||||||
|
// Refresh tasks list to show completed state
|
||||||
|
void listTasks();
|
||||||
|
} else {
|
||||||
|
setToolResult({
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Task ${taskStatus.status}: ${taskStatus.statusMessage || "No additional information"}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isError: true,
|
||||||
|
});
|
||||||
|
// Refresh tasks list to show failed/cancelled state
|
||||||
|
void listTasks();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Update status message while polling
|
||||||
|
// Safely extract any _meta from the original response (if present)
|
||||||
|
const pollingResponseMeta =
|
||||||
|
response &&
|
||||||
|
typeof response === "object" &&
|
||||||
|
"_meta" in (response as Record<string, unknown>)
|
||||||
|
? ((response as { _meta?: Record<string, unknown> })._meta ??
|
||||||
|
{})
|
||||||
|
: undefined;
|
||||||
|
setToolResult({
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Task status: ${taskStatus.status}${taskStatus.statusMessage ? ` - ${taskStatus.statusMessage}` : ""}. Polling...`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
_meta: {
|
||||||
|
...(pollingResponseMeta || {}),
|
||||||
|
"io.modelcontextprotocol/related-task": { taskId },
|
||||||
|
},
|
||||||
|
} as CompatibilityCallToolResult);
|
||||||
|
// Refresh tasks list to show progress
|
||||||
|
void listTasks();
|
||||||
|
}
|
||||||
|
} catch (pollingError) {
|
||||||
|
console.error("Error polling task status:", pollingError);
|
||||||
|
setToolResult({
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Error polling task status: ${pollingError instanceof Error ? pollingError.message : String(pollingError)}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isError: true,
|
||||||
|
});
|
||||||
|
taskCompleted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setIsPollingTask(false);
|
||||||
|
} else {
|
||||||
|
setToolResult(response as CompatibilityCallToolResult);
|
||||||
|
}
|
||||||
// Clear any validation errors since tool execution completed
|
// Clear any validation errors since tool execution completed
|
||||||
setErrors((prev) => ({ ...prev, tools: null }));
|
setErrors((prev) => ({ ...prev, tools: null }));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -891,6 +1127,37 @@ const App = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const listTasks = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const response = await listMcpTasks(nextTaskCursor);
|
||||||
|
setTasks(response.tasks);
|
||||||
|
setNextTaskCursor(response.nextCursor);
|
||||||
|
// Inline error clear to avoid extra dependency on clearError
|
||||||
|
setErrors((prev) => ({ ...prev, tasks: null }));
|
||||||
|
} catch (e) {
|
||||||
|
setErrors((prev) => ({
|
||||||
|
...prev,
|
||||||
|
tasks: (e as Error).message ?? String(e),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [listMcpTasks, nextTaskCursor]);
|
||||||
|
|
||||||
|
const cancelTask = async (taskId: string) => {
|
||||||
|
try {
|
||||||
|
const response = await cancelMcpTask(taskId);
|
||||||
|
setTasks((prev) => prev.map((t) => (t.taskId === taskId ? response : t)));
|
||||||
|
if (selectedTask?.taskId === taskId) {
|
||||||
|
setSelectedTask(response);
|
||||||
|
}
|
||||||
|
clearError("tasks");
|
||||||
|
} catch (e) {
|
||||||
|
setErrors((prev) => ({
|
||||||
|
...prev,
|
||||||
|
tasks: (e as Error).message ?? String(e),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleRootsChange = async () => {
|
const handleRootsChange = async () => {
|
||||||
await sendNotification({ method: "notifications/roots/list_changed" });
|
await sendNotification({ method: "notifications/roots/list_changed" });
|
||||||
};
|
};
|
||||||
|
|
@ -1034,6 +1301,13 @@ const App = () => {
|
||||||
<Hammer className="w-4 h-4 mr-2" />
|
<Hammer className="w-4 h-4 mr-2" />
|
||||||
Tools
|
Tools
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
value="tasks"
|
||||||
|
disabled={!serverCapabilities?.tasks}
|
||||||
|
>
|
||||||
|
<ListTodo className="w-4 h-4 mr-2" />
|
||||||
|
Tasks
|
||||||
|
</TabsTrigger>
|
||||||
<TabsTrigger value="ping">
|
<TabsTrigger value="ping">
|
||||||
<Bell className="w-4 h-4 mr-2" />
|
<Bell className="w-4 h-4 mr-2" />
|
||||||
Ping
|
Ping
|
||||||
|
|
@ -1182,10 +1456,11 @@ const App = () => {
|
||||||
name: string,
|
name: string,
|
||||||
params: Record<string, unknown>,
|
params: Record<string, unknown>,
|
||||||
metadata?: Record<string, unknown>,
|
metadata?: Record<string, unknown>,
|
||||||
|
runAsTask?: boolean,
|
||||||
) => {
|
) => {
|
||||||
clearError("tools");
|
clearError("tools");
|
||||||
setToolResult(null);
|
setToolResult(null);
|
||||||
await callTool(name, params, metadata);
|
await callTool(name, params, metadata, runAsTask);
|
||||||
}}
|
}}
|
||||||
selectedTool={selectedTool}
|
selectedTool={selectedTool}
|
||||||
setSelectedTool={(tool) => {
|
setSelectedTool={(tool) => {
|
||||||
|
|
@ -1194,6 +1469,7 @@ const App = () => {
|
||||||
setToolResult(null);
|
setToolResult(null);
|
||||||
}}
|
}}
|
||||||
toolResult={toolResult}
|
toolResult={toolResult}
|
||||||
|
isPollingTask={isPollingTask}
|
||||||
nextCursor={nextToolCursor}
|
nextCursor={nextToolCursor}
|
||||||
error={errors.tools}
|
error={errors.tools}
|
||||||
resourceContent={resourceContentMap}
|
resourceContent={resourceContentMap}
|
||||||
|
|
@ -1202,6 +1478,25 @@ const App = () => {
|
||||||
readResource(uri);
|
readResource(uri);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<TasksTab
|
||||||
|
tasks={tasks}
|
||||||
|
listTasks={() => {
|
||||||
|
clearError("tasks");
|
||||||
|
listTasks();
|
||||||
|
}}
|
||||||
|
clearTasks={() => {
|
||||||
|
setTasks([]);
|
||||||
|
setNextTaskCursor(undefined);
|
||||||
|
}}
|
||||||
|
cancelTask={cancelTask}
|
||||||
|
selectedTask={selectedTask}
|
||||||
|
setSelectedTask={(task) => {
|
||||||
|
clearError("tasks");
|
||||||
|
setSelectedTask(task);
|
||||||
|
}}
|
||||||
|
error={errors.tasks}
|
||||||
|
nextCursor={nextTaskCursor}
|
||||||
|
/>
|
||||||
<ConsoleTab />
|
<ConsoleTab />
|
||||||
<PingTab
|
<PingTab
|
||||||
onPingClick={() => {
|
onPingClick={() => {
|
||||||
|
|
|
||||||
239
client/src/__tests__/App.samplingNavigation.test.tsx
Normal file
239
client/src/__tests__/App.samplingNavigation.test.tsx
Normal file
|
|
@ -0,0 +1,239 @@
|
||||||
|
import {
|
||||||
|
act,
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
waitFor,
|
||||||
|
} from "@testing-library/react";
|
||||||
|
import App from "../App";
|
||||||
|
import { useConnection } from "../lib/hooks/useConnection";
|
||||||
|
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||||
|
import type {
|
||||||
|
CreateMessageRequest,
|
||||||
|
CreateMessageResult,
|
||||||
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
|
type OnPendingRequestHandler = (
|
||||||
|
request: CreateMessageRequest,
|
||||||
|
resolve: (result: CreateMessageResult) => void,
|
||||||
|
reject: (error: Error) => void,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
type SamplingRequestMockProps = {
|
||||||
|
request: { id: number };
|
||||||
|
onApprove: (id: number, result: CreateMessageResult) => void;
|
||||||
|
onReject: (id: number) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UseConnectionReturn = ReturnType<typeof useConnection>;
|
||||||
|
|
||||||
|
// Mock auth dependencies first
|
||||||
|
jest.mock("@modelcontextprotocol/sdk/client/auth.js", () => ({
|
||||||
|
auth: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("../lib/oauth-state-machine", () => ({
|
||||||
|
OAuthStateMachine: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("../lib/auth", () => ({
|
||||||
|
InspectorOAuthClientProvider: jest.fn().mockImplementation(() => ({
|
||||||
|
tokens: jest.fn().mockResolvedValue(null),
|
||||||
|
clear: jest.fn(),
|
||||||
|
})),
|
||||||
|
DebugInspectorOAuthClientProvider: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("../utils/configUtils", () => ({
|
||||||
|
...jest.requireActual("../utils/configUtils"),
|
||||||
|
getMCPProxyAddress: jest.fn(() => "http://localhost:6277"),
|
||||||
|
getMCPProxyAuthToken: jest.fn(() => ({
|
||||||
|
token: "",
|
||||||
|
header: "X-MCP-Proxy-Auth",
|
||||||
|
})),
|
||||||
|
getInitialTransportType: jest.fn(() => "stdio"),
|
||||||
|
getInitialSseUrl: jest.fn(() => "http://localhost:3001/sse"),
|
||||||
|
getInitialCommand: jest.fn(() => "mcp-server-everything"),
|
||||||
|
getInitialArgs: jest.fn(() => ""),
|
||||||
|
initializeInspectorConfig: jest.fn(() => ({})),
|
||||||
|
saveInspectorConfig: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("../lib/hooks/useDraggablePane", () => ({
|
||||||
|
useDraggablePane: () => ({
|
||||||
|
height: 300,
|
||||||
|
handleDragStart: jest.fn(),
|
||||||
|
}),
|
||||||
|
useDraggableSidebar: () => ({
|
||||||
|
width: 320,
|
||||||
|
isDragging: false,
|
||||||
|
handleDragStart: jest.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("../components/Sidebar", () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: () => <div>Sidebar</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("../lib/hooks/useToast", () => ({
|
||||||
|
useToast: () => ({ toast: jest.fn() }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Keep the test focused on navigation; avoid DynamicJsonForm/schema complexity.
|
||||||
|
jest.mock("../components/SamplingRequest", () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: ({ request, onApprove, onReject }: SamplingRequestMockProps) => (
|
||||||
|
<div data-testid="sampling-request">
|
||||||
|
<div>sampling-request-{request.id}</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
onApprove(request.id, {
|
||||||
|
model: "stub-model",
|
||||||
|
stopReason: "endTurn",
|
||||||
|
role: "assistant",
|
||||||
|
content: { type: "text", text: "" },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Approve
|
||||||
|
</button>
|
||||||
|
<button type="button" onClick={() => onReject(request.id)}>
|
||||||
|
Reject
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock fetch
|
||||||
|
global.fetch = jest.fn().mockResolvedValue({ json: () => Promise.resolve({}) });
|
||||||
|
|
||||||
|
jest.mock("../lib/hooks/useConnection", () => ({
|
||||||
|
useConnection: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("App - Sampling auto-navigation", () => {
|
||||||
|
const mockUseConnection = jest.mocked(useConnection);
|
||||||
|
|
||||||
|
const baseConnectionState = {
|
||||||
|
connectionStatus: "connected" as const,
|
||||||
|
serverCapabilities: { tools: { listChanged: true, subscribe: true } },
|
||||||
|
mcpClient: {
|
||||||
|
request: jest.fn(),
|
||||||
|
notification: jest.fn(),
|
||||||
|
close: jest.fn(),
|
||||||
|
} as unknown as Client,
|
||||||
|
requestHistory: [],
|
||||||
|
clearRequestHistory: jest.fn(),
|
||||||
|
makeRequest: jest.fn(),
|
||||||
|
sendNotification: jest.fn(),
|
||||||
|
handleCompletion: jest.fn(),
|
||||||
|
completionsSupported: false,
|
||||||
|
connect: jest.fn(),
|
||||||
|
disconnect: jest.fn(),
|
||||||
|
serverImplementation: null,
|
||||||
|
cancelTask: jest.fn(),
|
||||||
|
listTasks: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
window.location.hash = "#tools";
|
||||||
|
});
|
||||||
|
|
||||||
|
test("switches to #sampling when a sampling request arrives and switches back to #tools after approve", async () => {
|
||||||
|
let capturedOnPendingRequest: OnPendingRequestHandler | undefined;
|
||||||
|
|
||||||
|
mockUseConnection.mockImplementation((options) => {
|
||||||
|
capturedOnPendingRequest = (
|
||||||
|
options as { onPendingRequest?: OnPendingRequestHandler }
|
||||||
|
).onPendingRequest;
|
||||||
|
return baseConnectionState as unknown as UseConnectionReturn;
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<App />);
|
||||||
|
|
||||||
|
// Ensure we start on tools.
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(window.location.hash).toBe("#tools");
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolve = jest.fn();
|
||||||
|
const reject = jest.fn();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
if (!capturedOnPendingRequest) {
|
||||||
|
throw new Error("Expected onPendingRequest to be provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
capturedOnPendingRequest(
|
||||||
|
{
|
||||||
|
method: "sampling/createMessage",
|
||||||
|
params: { messages: [], maxTokens: 1 },
|
||||||
|
},
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(window.location.hash).toBe("#sampling");
|
||||||
|
expect(screen.getByTestId("sampling-request")).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByText("Approve"));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(resolve).toHaveBeenCalled();
|
||||||
|
expect(window.location.hash).toBe("#tools");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("switches back to #tools after reject", async () => {
|
||||||
|
let capturedOnPendingRequest: OnPendingRequestHandler | undefined;
|
||||||
|
|
||||||
|
mockUseConnection.mockImplementation((options) => {
|
||||||
|
capturedOnPendingRequest = (
|
||||||
|
options as { onPendingRequest?: OnPendingRequestHandler }
|
||||||
|
).onPendingRequest;
|
||||||
|
return baseConnectionState as unknown as UseConnectionReturn;
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<App />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(window.location.hash).toBe("#tools");
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolve = jest.fn();
|
||||||
|
const reject = jest.fn();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
if (!capturedOnPendingRequest) {
|
||||||
|
throw new Error("Expected onPendingRequest to be provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
capturedOnPendingRequest(
|
||||||
|
{
|
||||||
|
method: "sampling/createMessage",
|
||||||
|
params: { messages: [], maxTokens: 1 },
|
||||||
|
},
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(window.location.hash).toBe("#sampling");
|
||||||
|
expect(screen.getByTestId("sampling-request")).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByText("Reject"));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(reject).toHaveBeenCalled();
|
||||||
|
expect(window.location.hash).toBe("#tools");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -9,6 +9,7 @@ import SamplingRequest from "./SamplingRequest";
|
||||||
export type PendingRequest = {
|
export type PendingRequest = {
|
||||||
id: number;
|
id: number;
|
||||||
request: CreateMessageRequest;
|
request: CreateMessageRequest;
|
||||||
|
originatingTab?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
|
|
|
||||||
229
client/src/components/TasksTab.tsx
Normal file
229
client/src/components/TasksTab.tsx
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
|
import { Task } from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
import {
|
||||||
|
AlertCircle,
|
||||||
|
RefreshCw,
|
||||||
|
XCircle,
|
||||||
|
Clock,
|
||||||
|
CheckCircle2,
|
||||||
|
AlertTriangle,
|
||||||
|
PlayCircle,
|
||||||
|
} from "lucide-react";
|
||||||
|
import ListPane from "./ListPane";
|
||||||
|
import { useState } from "react";
|
||||||
|
import JsonView from "./JsonView";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const TaskStatusIcon = ({ status }: { status: Task["status"] }) => {
|
||||||
|
switch (status) {
|
||||||
|
case "working":
|
||||||
|
return <Clock className="h-4 w-4 animate-pulse text-blue-500" />;
|
||||||
|
case "input_required":
|
||||||
|
return <AlertTriangle className="h-4 w-4 text-yellow-500" />;
|
||||||
|
case "completed":
|
||||||
|
return <CheckCircle2 className="h-4 w-4 text-green-500" />;
|
||||||
|
case "failed":
|
||||||
|
return <XCircle className="h-4 w-4 text-red-500" />;
|
||||||
|
case "cancelled":
|
||||||
|
return <XCircle className="h-4 w-4 text-gray-500" />;
|
||||||
|
default:
|
||||||
|
return <PlayCircle className="h-4 w-4" />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const TasksTab = ({
|
||||||
|
tasks,
|
||||||
|
listTasks,
|
||||||
|
clearTasks,
|
||||||
|
cancelTask,
|
||||||
|
selectedTask,
|
||||||
|
setSelectedTask,
|
||||||
|
error,
|
||||||
|
nextCursor,
|
||||||
|
}: {
|
||||||
|
tasks: Task[];
|
||||||
|
listTasks: () => void;
|
||||||
|
clearTasks: () => void;
|
||||||
|
cancelTask: (taskId: string) => Promise<void>;
|
||||||
|
selectedTask: Task | null;
|
||||||
|
setSelectedTask: (task: Task | null) => void;
|
||||||
|
error: string | null;
|
||||||
|
nextCursor?: string;
|
||||||
|
}) => {
|
||||||
|
const [isCancelling, setIsCancelling] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const displayedTask = selectedTask
|
||||||
|
? tasks.find((t) => t.taskId === selectedTask.taskId) || selectedTask
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const handleCancel = async (taskId: string) => {
|
||||||
|
setIsCancelling(taskId);
|
||||||
|
try {
|
||||||
|
await cancelTask(taskId);
|
||||||
|
} finally {
|
||||||
|
setIsCancelling(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TabsContent value="tasks" className="flex-1 overflow-hidden p-0 m-0">
|
||||||
|
<div className="flex h-full overflow-hidden p-4 gap-4">
|
||||||
|
<div className="w-1/3">
|
||||||
|
<ListPane
|
||||||
|
title="Tasks"
|
||||||
|
items={tasks}
|
||||||
|
setSelectedItem={setSelectedTask}
|
||||||
|
listItems={listTasks}
|
||||||
|
clearItems={clearTasks}
|
||||||
|
buttonText={nextCursor ? "List More Tasks" : "List Tasks"}
|
||||||
|
isButtonDisabled={!nextCursor && tasks.length > 0}
|
||||||
|
renderItem={(task) => (
|
||||||
|
<div className="flex items-center gap-2 overflow-hidden w-full">
|
||||||
|
<TaskStatusIcon status={task.status} />
|
||||||
|
<div className="flex flex-col overflow-hidden">
|
||||||
|
<span className="truncate font-medium">{task.taskId}</span>
|
||||||
|
<span className="truncate text-xs text-muted-foreground">
|
||||||
|
{task.status} -{" "}
|
||||||
|
{new Date(task.lastUpdatedAt).toLocaleString()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 overflow-y-auto p-4 bg-background border border-border rounded-lg">
|
||||||
|
{error && (
|
||||||
|
<Alert variant="destructive" className="mb-4">
|
||||||
|
<AlertCircle className="h-4 w-4" />
|
||||||
|
<AlertTitle>Error</AlertTitle>
|
||||||
|
<AlertDescription>{error}</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{displayedTask ? (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="flex items-center justify-between border-b pb-4">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold tracking-tight">
|
||||||
|
Task Details
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
ID: {displayedTask.taskId}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{(displayedTask.status === "working" ||
|
||||||
|
displayedTask.status === "input_required") && (
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
size="sm"
|
||||||
|
aria-label={`Cancel task ${displayedTask.taskId}`}
|
||||||
|
onClick={() => handleCancel(displayedTask.taskId)}
|
||||||
|
disabled={isCancelling === displayedTask.taskId}
|
||||||
|
>
|
||||||
|
{isCancelling === displayedTask.taskId ? (
|
||||||
|
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<XCircle className="mr-2 h-4 w-4" />
|
||||||
|
)}
|
||||||
|
Cancel Task
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div className="rounded-lg border p-3">
|
||||||
|
<p className="text-sm font-medium text-muted-foreground">
|
||||||
|
Status
|
||||||
|
</p>
|
||||||
|
<div className="mt-1 flex items-center gap-2">
|
||||||
|
<TaskStatusIcon status={displayedTask.status} />
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"font-semibold capitalize",
|
||||||
|
displayedTask.status === "working" && "text-blue-500",
|
||||||
|
displayedTask.status === "completed" &&
|
||||||
|
"text-green-500",
|
||||||
|
displayedTask.status === "failed" && "text-red-500",
|
||||||
|
displayedTask.status === "cancelled" && "text-gray-500",
|
||||||
|
displayedTask.status === "input_required" &&
|
||||||
|
"text-yellow-500",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{displayedTask.status.replace("_", " ")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg border p-3">
|
||||||
|
<p className="text-sm font-medium text-muted-foreground">
|
||||||
|
Last Updated
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 font-medium">
|
||||||
|
{new Date(displayedTask.lastUpdatedAt).toLocaleString()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg border p-3">
|
||||||
|
<p className="text-sm font-medium text-muted-foreground">
|
||||||
|
Created At
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 font-medium">
|
||||||
|
{new Date(displayedTask.createdAt).toLocaleString()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg border p-3">
|
||||||
|
<p className="text-sm font-medium text-muted-foreground">
|
||||||
|
TTL
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 font-medium">
|
||||||
|
{displayedTask.ttl === null
|
||||||
|
? "Infinite"
|
||||||
|
: `${displayedTask.ttl}ms`}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{displayedTask.statusMessage && (
|
||||||
|
<div className="rounded-lg border p-3">
|
||||||
|
<p className="text-sm font-medium text-muted-foreground">
|
||||||
|
Status Message
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 whitespace-pre-wrap">
|
||||||
|
{displayedTask.statusMessage}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h3 className="text-lg font-semibold">Full Task Object</h3>
|
||||||
|
<div className="rounded-md border">
|
||||||
|
<JsonView data={displayedTask} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full items-center justify-center text-muted-foreground">
|
||||||
|
<div className="text-center">
|
||||||
|
<Clock className="mx-auto mb-4 h-12 w-12 opacity-20" />
|
||||||
|
<h3 className="text-lg font-medium">No Task Selected</h3>
|
||||||
|
<p>Select a task from the list to view its details.</p>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="mt-4"
|
||||||
|
onClick={listTasks}
|
||||||
|
>
|
||||||
|
<RefreshCw className="mr-2 h-4 w-4" />
|
||||||
|
Refresh Tasks
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TasksTab;
|
||||||
|
|
@ -12,6 +12,7 @@ interface ToolResultsProps {
|
||||||
selectedTool: Tool | null;
|
selectedTool: Tool | null;
|
||||||
resourceContent: Record<string, string>;
|
resourceContent: Record<string, string>;
|
||||||
onReadResource?: (uri: string) => void;
|
onReadResource?: (uri: string) => void;
|
||||||
|
isPollingTask?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkContentCompatibility = (
|
const checkContentCompatibility = (
|
||||||
|
|
@ -69,6 +70,7 @@ const ToolResults = ({
|
||||||
selectedTool,
|
selectedTool,
|
||||||
resourceContent,
|
resourceContent,
|
||||||
onReadResource,
|
onReadResource,
|
||||||
|
isPollingTask,
|
||||||
}: ToolResultsProps) => {
|
}: ToolResultsProps) => {
|
||||||
if (!toolResult) return null;
|
if (!toolResult) return null;
|
||||||
|
|
||||||
|
|
@ -89,6 +91,19 @@ const ToolResults = ({
|
||||||
const structuredResult = parsedResult.data;
|
const structuredResult = parsedResult.data;
|
||||||
const isError = structuredResult.isError ?? false;
|
const isError = structuredResult.isError ?? false;
|
||||||
|
|
||||||
|
// Check if this is a running task
|
||||||
|
const relatedTask = structuredResult._meta?.[
|
||||||
|
"io.modelcontextprotocol/related-task"
|
||||||
|
] as { taskId: string } | undefined;
|
||||||
|
const isTaskRunning =
|
||||||
|
isPollingTask ||
|
||||||
|
(!!relatedTask &&
|
||||||
|
structuredResult.content.some(
|
||||||
|
(c) =>
|
||||||
|
c.type === "text" &&
|
||||||
|
(c.text?.includes("Polling") || c.text?.includes("Task status")),
|
||||||
|
));
|
||||||
|
|
||||||
let validationResult = null;
|
let validationResult = null;
|
||||||
const toolHasOutputSchema =
|
const toolHasOutputSchema =
|
||||||
selectedTool && hasOutputSchema(selectedTool.name);
|
selectedTool && hasOutputSchema(selectedTool.name);
|
||||||
|
|
@ -127,6 +142,8 @@ const ToolResults = ({
|
||||||
Tool Result:{" "}
|
Tool Result:{" "}
|
||||||
{isError ? (
|
{isError ? (
|
||||||
<span className="text-red-600 font-semibold">Error</span>
|
<span className="text-red-600 font-semibold">Error</span>
|
||||||
|
) : isTaskRunning ? (
|
||||||
|
<span className="text-yellow-600 font-semibold">Task Running</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-green-600 font-semibold">Success</span>
|
<span className="text-green-600 font-semibold">Success</span>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ const ToolsTab = ({
|
||||||
selectedTool,
|
selectedTool,
|
||||||
setSelectedTool,
|
setSelectedTool,
|
||||||
toolResult,
|
toolResult,
|
||||||
|
isPollingTask,
|
||||||
nextCursor,
|
nextCursor,
|
||||||
error,
|
error,
|
||||||
resourceContent,
|
resourceContent,
|
||||||
|
|
@ -76,16 +77,19 @@ const ToolsTab = ({
|
||||||
name: string,
|
name: string,
|
||||||
params: Record<string, unknown>,
|
params: Record<string, unknown>,
|
||||||
metadata?: Record<string, unknown>,
|
metadata?: Record<string, unknown>,
|
||||||
|
runAsTask?: boolean,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
selectedTool: Tool | null;
|
selectedTool: Tool | null;
|
||||||
setSelectedTool: (tool: Tool | null) => void;
|
setSelectedTool: (tool: Tool | null) => void;
|
||||||
toolResult: CompatibilityCallToolResult | null;
|
toolResult: CompatibilityCallToolResult | null;
|
||||||
|
isPollingTask?: boolean;
|
||||||
nextCursor: ListToolsResult["nextCursor"];
|
nextCursor: ListToolsResult["nextCursor"];
|
||||||
error: string | null;
|
error: string | null;
|
||||||
resourceContent: Record<string, string>;
|
resourceContent: Record<string, string>;
|
||||||
onReadResource?: (uri: string) => void;
|
onReadResource?: (uri: string) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [params, setParams] = useState<Record<string, unknown>>({});
|
const [params, setParams] = useState<Record<string, unknown>>({});
|
||||||
|
const [runAsTask, setRunAsTask] = useState(false);
|
||||||
const [isToolRunning, setIsToolRunning] = useState(false);
|
const [isToolRunning, setIsToolRunning] = useState(false);
|
||||||
const [isOutputSchemaExpanded, setIsOutputSchemaExpanded] = useState(false);
|
const [isOutputSchemaExpanded, setIsOutputSchemaExpanded] = useState(false);
|
||||||
const [isMetadataExpanded, setIsMetadataExpanded] = useState(false);
|
const [isMetadataExpanded, setIsMetadataExpanded] = useState(false);
|
||||||
|
|
@ -125,6 +129,7 @@ const ToolsTab = ({
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
setParams(Object.fromEntries(params));
|
setParams(Object.fromEntries(params));
|
||||||
|
setRunAsTask(false);
|
||||||
|
|
||||||
// Reset validation errors when switching tools
|
// Reset validation errors when switching tools
|
||||||
setHasValidationErrors(false);
|
setHasValidationErrors(false);
|
||||||
|
|
@ -157,6 +162,7 @@ const ToolsTab = ({
|
||||||
clearItems={() => {
|
clearItems={() => {
|
||||||
clearTools();
|
clearTools();
|
||||||
setSelectedTool(null);
|
setSelectedTool(null);
|
||||||
|
setRunAsTask(false);
|
||||||
}}
|
}}
|
||||||
setSelectedItem={setSelectedTool}
|
setSelectedItem={setSelectedTool}
|
||||||
renderItem={(tool) => (
|
renderItem={(tool) => (
|
||||||
|
|
@ -651,6 +657,21 @@ const ToolsTab = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="run-as-task"
|
||||||
|
checked={runAsTask}
|
||||||
|
onCheckedChange={(checked: boolean) =>
|
||||||
|
setRunAsTask(checked)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor="run-as-task"
|
||||||
|
className="text-sm font-medium text-gray-700 dark:text-gray-300 cursor-pointer"
|
||||||
|
>
|
||||||
|
Run as task
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
// Validate JSON inputs before calling tool
|
// Validate JSON inputs before calling tool
|
||||||
|
|
@ -676,6 +697,7 @@ const ToolsTab = ({
|
||||||
selectedTool.name,
|
selectedTool.name,
|
||||||
params,
|
params,
|
||||||
Object.keys(metadata).length ? metadata : undefined,
|
Object.keys(metadata).length ? metadata : undefined,
|
||||||
|
runAsTask,
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
setIsToolRunning(false);
|
setIsToolRunning(false);
|
||||||
|
|
@ -683,16 +705,17 @@ const ToolsTab = ({
|
||||||
}}
|
}}
|
||||||
disabled={
|
disabled={
|
||||||
isToolRunning ||
|
isToolRunning ||
|
||||||
|
isPollingTask ||
|
||||||
hasValidationErrors ||
|
hasValidationErrors ||
|
||||||
hasReservedMetadataEntry ||
|
hasReservedMetadataEntry ||
|
||||||
hasInvalidMetaPrefixEntry ||
|
hasInvalidMetaPrefixEntry ||
|
||||||
hasInvalidMetaNameEntry
|
hasInvalidMetaNameEntry
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{isToolRunning ? (
|
{isToolRunning || isPollingTask ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||||
Running...
|
{isPollingTask ? "Polling Task..." : "Running..."}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
@ -731,6 +754,7 @@ const ToolsTab = ({
|
||||||
selectedTool={selectedTool}
|
selectedTool={selectedTool}
|
||||||
resourceContent={resourceContent}
|
resourceContent={resourceContent}
|
||||||
onReadResource={onReadResource}
|
onReadResource={onReadResource}
|
||||||
|
isPollingTask={isPollingTask}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@ import "@testing-library/jest-dom";
|
||||||
import { describe, it, jest, beforeEach } from "@jest/globals";
|
import { describe, it, jest, beforeEach } from "@jest/globals";
|
||||||
import ToolsTab from "../ToolsTab";
|
import ToolsTab from "../ToolsTab";
|
||||||
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { Tabs } from "@/components/ui/tabs";
|
import { Tabs } from "../ui/tabs";
|
||||||
import { cacheToolOutputSchemas } from "@/utils/schemaUtils";
|
import { cacheToolOutputSchemas } from "../../utils/schemaUtils";
|
||||||
import { within } from "@testing-library/react";
|
import { within } from "@testing-library/react";
|
||||||
import {
|
import {
|
||||||
META_NAME_RULES_MESSAGE,
|
META_NAME_RULES_MESSAGE,
|
||||||
META_PREFIX_RULES_MESSAGE,
|
META_PREFIX_RULES_MESSAGE,
|
||||||
RESERVED_NAMESPACE_MESSAGE,
|
RESERVED_NAMESPACE_MESSAGE,
|
||||||
} from "@/utils/metaUtils";
|
} from "../../utils/metaUtils";
|
||||||
|
|
||||||
describe("ToolsTab", () => {
|
describe("ToolsTab", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
@ -130,6 +130,7 @@ describe("ToolsTab", () => {
|
||||||
count: 42,
|
count: 42,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -155,6 +156,7 @@ describe("ToolsTab", () => {
|
||||||
num: -42,
|
num: -42,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -191,6 +193,7 @@ describe("ToolsTab", () => {
|
||||||
num: null,
|
num: null,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -234,6 +237,7 @@ describe("ToolsTab", () => {
|
||||||
optionalBoolean: null,
|
optionalBoolean: null,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// State 2: Uncheck null checkbox -> should set value to false and enable input
|
// State 2: Uncheck null checkbox -> should set value to false and enable input
|
||||||
|
|
@ -255,6 +259,7 @@ describe("ToolsTab", () => {
|
||||||
optionalBoolean: false,
|
optionalBoolean: false,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// State 3: Check boolean checkbox -> should set value to true
|
// State 3: Check boolean checkbox -> should set value to true
|
||||||
|
|
@ -277,6 +282,7 @@ describe("ToolsTab", () => {
|
||||||
optionalBoolean: true,
|
optionalBoolean: true,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// State 4: Check null checkbox again -> should set value back to null and disable input
|
// State 4: Check null checkbox again -> should set value back to null and disable input
|
||||||
|
|
@ -295,6 +301,7 @@ describe("ToolsTab", () => {
|
||||||
optionalBoolean: null,
|
optionalBoolean: null,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -816,6 +823,7 @@ describe("ToolsTab", () => {
|
||||||
mockTools[0].name,
|
mockTools[0].name,
|
||||||
expect.any(Object),
|
expect.any(Object),
|
||||||
{ requestId: "abc123" },
|
{ requestId: "abc123" },
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -1080,6 +1088,7 @@ describe("ToolsTab", () => {
|
||||||
count: 5,
|
count: 5,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,4 +40,9 @@ export type InspectorConfig = {
|
||||||
* Session token for authenticating with the MCP Proxy Server. This token is displayed in the proxy server console on startup.
|
* Session token for authenticating with the MCP Proxy Server. This token is displayed in the proxy server console on startup.
|
||||||
*/
|
*/
|
||||||
MCP_PROXY_AUTH_TOKEN: ConfigItem;
|
MCP_PROXY_AUTH_TOKEN: ConfigItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Time-to-Live (TTL) in milliseconds for newly created tasks.
|
||||||
|
*/
|
||||||
|
MCP_TASK_TTL: ConfigItem;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -76,4 +76,11 @@ export const DEFAULT_INSPECTOR_CONFIG: InspectorConfig = {
|
||||||
value: "",
|
value: "",
|
||||||
is_session_item: true,
|
is_session_item: true,
|
||||||
},
|
},
|
||||||
|
MCP_TASK_TTL: {
|
||||||
|
label: "Task TTL",
|
||||||
|
description:
|
||||||
|
"Default Time-to-Live (TTL) in milliseconds for newly created tasks",
|
||||||
|
value: 60000,
|
||||||
|
is_session_item: false,
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
import { renderHook, act } from "@testing-library/react";
|
import { renderHook, act } from "@testing-library/react";
|
||||||
import { useConnection } from "../useConnection";
|
import { useConnection } from "../useConnection";
|
||||||
import { z } from "zod";
|
import { z } from "zod/v3";
|
||||||
import {
|
import {
|
||||||
ClientRequest,
|
ClientRequest,
|
||||||
|
CreateTaskResultSchema,
|
||||||
JSONRPCMessage,
|
JSONRPCMessage,
|
||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
import type {
|
||||||
|
AnySchema,
|
||||||
|
SchemaOutput,
|
||||||
|
} from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
||||||
import { DEFAULT_INSPECTOR_CONFIG, CLIENT_IDENTITY } from "../../constants";
|
import { DEFAULT_INSPECTOR_CONFIG, CLIENT_IDENTITY } from "../../constants";
|
||||||
import {
|
import {
|
||||||
SSEClientTransportOptions,
|
SSEClientTransportOptions,
|
||||||
|
|
@ -128,7 +133,7 @@ const mockDiscoverScopes = discoverScopes as jest.MockedFunction<
|
||||||
>;
|
>;
|
||||||
|
|
||||||
describe("useConnection", () => {
|
describe("useConnection", () => {
|
||||||
const defaultProps = {
|
const defaultProps: Parameters<typeof useConnection>[0] = {
|
||||||
transportType: "sse" as const,
|
transportType: "sse" as const,
|
||||||
command: "",
|
command: "",
|
||||||
args: "",
|
args: "",
|
||||||
|
|
@ -164,8 +169,10 @@ describe("useConnection", () => {
|
||||||
test: z.string(),
|
test: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockSchemaAny: AnySchema = mockSchema as unknown as AnySchema;
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await result.current.makeRequest(mockRequest, mockSchema);
|
await result.current.makeRequest(mockRequest, mockSchemaAny);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockClient.request).toHaveBeenCalledWith(
|
expect(mockClient.request).toHaveBeenCalledWith(
|
||||||
|
|
@ -204,8 +211,10 @@ describe("useConnection", () => {
|
||||||
test: z.string(),
|
test: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockSchemaAny: AnySchema = mockSchema as unknown as AnySchema;
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await result.current.makeRequest(mockRequest, mockSchema, {
|
await result.current.makeRequest(mockRequest, mockSchemaAny, {
|
||||||
timeout: 1000,
|
timeout: 1000,
|
||||||
maxTotalTimeout: 2000,
|
maxTotalTimeout: 2000,
|
||||||
resetTimeoutOnProgress: false,
|
resetTimeoutOnProgress: false,
|
||||||
|
|
@ -224,8 +233,251 @@ describe("useConnection", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Receiver-side Tasks (task-augmented incoming requests)", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("declares tasks.requests.sampling.createMessage when onPendingRequest is provided", async () => {
|
||||||
|
const Client = jest.requireMock(
|
||||||
|
"@modelcontextprotocol/sdk/client/index.js",
|
||||||
|
).Client;
|
||||||
|
|
||||||
|
const propsWithPending = {
|
||||||
|
...defaultProps,
|
||||||
|
onPendingRequest: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useConnection(propsWithPending));
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await result.current.connect();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Client).toHaveBeenCalledWith(
|
||||||
|
expect.any(Object),
|
||||||
|
expect.objectContaining({
|
||||||
|
capabilities: expect.objectContaining({
|
||||||
|
tasks: expect.objectContaining({
|
||||||
|
requests: expect.objectContaining({
|
||||||
|
sampling: expect.objectContaining({
|
||||||
|
createMessage: {},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("task-augmented sampling/createMessage returns { task } and tasks/result blocks until resolved", async () => {
|
||||||
|
let pendingResolve: ((value: unknown) => void) | undefined;
|
||||||
|
let pendingReject: ((reason?: unknown) => void) | undefined;
|
||||||
|
|
||||||
|
const mockOnPendingRequest = jest.fn((_request, resolve, reject) => {
|
||||||
|
pendingResolve = resolve;
|
||||||
|
pendingReject = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
const propsWithPending = {
|
||||||
|
...defaultProps,
|
||||||
|
onPendingRequest: mockOnPendingRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useConnection(propsWithPending));
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await result.current.connect();
|
||||||
|
});
|
||||||
|
|
||||||
|
const samplingRequest = {
|
||||||
|
method: "sampling/createMessage",
|
||||||
|
params: {
|
||||||
|
task: { ttl: 0 },
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: { type: "text", text: "hello" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
maxTokens: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Locate the sampling/createMessage handler
|
||||||
|
const samplingHandlerCall = mockClient.setRequestHandler.mock.calls.find(
|
||||||
|
(call) => {
|
||||||
|
try {
|
||||||
|
const schema = call[0];
|
||||||
|
const parseResult =
|
||||||
|
schema.safeParse && schema.safeParse(samplingRequest);
|
||||||
|
return parseResult?.success;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(samplingHandlerCall).toBeDefined();
|
||||||
|
const [, samplingHandler] = samplingHandlerCall;
|
||||||
|
|
||||||
|
// Invoke handler; should return a CreateTaskResult immediately
|
||||||
|
let createTaskResult: SchemaOutput<typeof CreateTaskResultSchema>;
|
||||||
|
await act(async () => {
|
||||||
|
createTaskResult = await samplingHandler(samplingRequest);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createTaskResult).toHaveProperty("task");
|
||||||
|
expect(createTaskResult.task).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
taskId: expect.any(String),
|
||||||
|
status: "input_required",
|
||||||
|
ttl: 0,
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
lastUpdatedAt: expect.any(String),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockOnPendingRequest).toHaveBeenCalledTimes(1);
|
||||||
|
expect(pendingResolve).toBeDefined();
|
||||||
|
expect(pendingReject).toBeDefined();
|
||||||
|
|
||||||
|
const taskId = createTaskResult.task.taskId as string;
|
||||||
|
|
||||||
|
// Locate tasks/get and tasks/result handlers
|
||||||
|
const taskGetRequest = { method: "tasks/get", params: { taskId } };
|
||||||
|
const taskResultRequest = { method: "tasks/result", params: { taskId } };
|
||||||
|
|
||||||
|
const taskGetHandlerCall = mockClient.setRequestHandler.mock.calls.find(
|
||||||
|
(call) => {
|
||||||
|
try {
|
||||||
|
const schema = call[0];
|
||||||
|
const parseResult =
|
||||||
|
schema.safeParse && schema.safeParse(taskGetRequest);
|
||||||
|
return parseResult?.success;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const taskResultHandlerCall =
|
||||||
|
mockClient.setRequestHandler.mock.calls.find((call) => {
|
||||||
|
try {
|
||||||
|
const schema = call[0];
|
||||||
|
const parseResult =
|
||||||
|
schema.safeParse && schema.safeParse(taskResultRequest);
|
||||||
|
return parseResult?.success;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(taskGetHandlerCall).toBeDefined();
|
||||||
|
expect(taskResultHandlerCall).toBeDefined();
|
||||||
|
|
||||||
|
const [, taskGetHandler] = taskGetHandlerCall;
|
||||||
|
const [, taskResultHandler] = taskResultHandlerCall;
|
||||||
|
|
||||||
|
// Verify tasks/get sees the in-progress task
|
||||||
|
const getBefore = await taskGetHandler(taskGetRequest);
|
||||||
|
expect(getBefore.status).toBe("input_required");
|
||||||
|
|
||||||
|
// tasks/result should block until user flow resolves
|
||||||
|
const payloadPromise = taskResultHandler(taskResultRequest);
|
||||||
|
const race = await Promise.race([
|
||||||
|
payloadPromise.then(() => "resolved"),
|
||||||
|
new Promise((r) => setTimeout(() => r("timeout"), 10)),
|
||||||
|
]);
|
||||||
|
expect(race).toBe("timeout");
|
||||||
|
|
||||||
|
const mockPayload = {
|
||||||
|
model: "test-model",
|
||||||
|
role: "assistant",
|
||||||
|
content: { type: "text", text: "ok" },
|
||||||
|
};
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
pendingResolve!(mockPayload);
|
||||||
|
// Let the background updater run
|
||||||
|
await new Promise((r) => setTimeout(r, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(payloadPromise).resolves.toEqual(mockPayload);
|
||||||
|
|
||||||
|
const getAfter = await taskGetHandler(taskGetRequest);
|
||||||
|
expect(getAfter.status).toBe("completed");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("task-augmented elicitation/create returns { task } immediately", async () => {
|
||||||
|
const mockOnElicitationRequest = jest.fn();
|
||||||
|
const propsWithElicitation = {
|
||||||
|
...defaultProps,
|
||||||
|
onElicitationRequest: mockOnElicitationRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useConnection(propsWithElicitation));
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await result.current.connect();
|
||||||
|
});
|
||||||
|
|
||||||
|
const elicitationRequest = {
|
||||||
|
method: "elicitation/create",
|
||||||
|
params: {
|
||||||
|
task: { ttl: 0 },
|
||||||
|
message: "Please provide your name",
|
||||||
|
requestedSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
name: { type: "string" },
|
||||||
|
},
|
||||||
|
required: ["name"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const elicitRequestHandlerCall =
|
||||||
|
mockClient.setRequestHandler.mock.calls.find((call) => {
|
||||||
|
try {
|
||||||
|
const schema = call[0];
|
||||||
|
const parseResult =
|
||||||
|
schema.safeParse && schema.safeParse(elicitationRequest);
|
||||||
|
return parseResult?.success;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(elicitRequestHandlerCall).toBeDefined();
|
||||||
|
const [, handler] = elicitRequestHandlerCall;
|
||||||
|
|
||||||
|
mockOnElicitationRequest.mockImplementation((_request, resolve) => {
|
||||||
|
resolve({ action: "accept", content: { name: "test" } });
|
||||||
|
});
|
||||||
|
|
||||||
|
const resultValue = await handler(elicitationRequest);
|
||||||
|
|
||||||
|
expect(resultValue).toHaveProperty("task");
|
||||||
|
expect(resultValue.task).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
taskId: expect.any(String),
|
||||||
|
status: "input_required",
|
||||||
|
ttl: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test("throws error when mcpClient is not connected", async () => {
|
test("throws error when mcpClient is not connected", async () => {
|
||||||
const { result } = renderHook(() => useConnection(defaultProps));
|
const { result } = renderHook(() => {
|
||||||
|
const { makeRequest } = useConnection(defaultProps) as unknown as {
|
||||||
|
makeRequest: (
|
||||||
|
request: ClientRequest,
|
||||||
|
schema: AnySchema,
|
||||||
|
) => Promise<unknown>;
|
||||||
|
};
|
||||||
|
return { makeRequest };
|
||||||
|
});
|
||||||
|
|
||||||
const mockRequest: ClientRequest = {
|
const mockRequest: ClientRequest = {
|
||||||
method: "ping",
|
method: "ping",
|
||||||
|
|
@ -236,8 +488,10 @@ describe("useConnection", () => {
|
||||||
test: z.string(),
|
test: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockSchemaAny: AnySchema = mockSchema as unknown as AnySchema;
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
result.current.makeRequest(mockRequest, mockSchema),
|
result.current.makeRequest(mockRequest, mockSchemaAny),
|
||||||
).rejects.toThrow("MCP client not connected");
|
).rejects.toThrow("MCP client not connected");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
import {
|
import {
|
||||||
ClientNotification,
|
ClientNotification,
|
||||||
ClientRequest,
|
ClientRequest,
|
||||||
|
ClientResult,
|
||||||
CreateMessageRequestSchema,
|
CreateMessageRequestSchema,
|
||||||
ListRootsRequestSchema,
|
ListRootsRequestSchema,
|
||||||
ResourceUpdatedNotificationSchema,
|
ResourceUpdatedNotificationSchema,
|
||||||
|
|
@ -32,13 +33,22 @@ import {
|
||||||
LoggingLevel,
|
LoggingLevel,
|
||||||
ElicitRequestSchema,
|
ElicitRequestSchema,
|
||||||
Implementation,
|
Implementation,
|
||||||
|
Task,
|
||||||
|
CreateTaskResultSchema,
|
||||||
|
GetTaskRequestSchema,
|
||||||
|
GetTaskPayloadRequestSchema,
|
||||||
|
ListTasksRequestSchema,
|
||||||
|
CancelTaskRequestSchema,
|
||||||
|
ListTasksResultSchema,
|
||||||
|
CancelTaskResultSchema,
|
||||||
|
TaskStatusNotificationSchema,
|
||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
import type {
|
import type {
|
||||||
AnySchema,
|
AnySchema,
|
||||||
SchemaOutput,
|
SchemaOutput,
|
||||||
} from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
} from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
||||||
import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
import { useToast } from "@/lib/hooks/useToast";
|
||||||
import { ConnectionStatus, CLIENT_IDENTITY } from "../constants";
|
import { ConnectionStatus, CLIENT_IDENTITY } from "../constants";
|
||||||
import { Notification } from "../notificationTypes";
|
import { Notification } from "../notificationTypes";
|
||||||
|
|
@ -56,6 +66,7 @@ import {
|
||||||
} from "../auth";
|
} from "../auth";
|
||||||
import {
|
import {
|
||||||
getMCPProxyAddress,
|
getMCPProxyAddress,
|
||||||
|
getMCPTaskTtl,
|
||||||
getMCPServerRequestMaxTotalTimeout,
|
getMCPServerRequestMaxTotalTimeout,
|
||||||
resetRequestTimeoutOnProgress,
|
resetRequestTimeoutOnProgress,
|
||||||
getMCPProxyAuthToken,
|
getMCPProxyAuthToken,
|
||||||
|
|
@ -131,6 +142,18 @@ export function useConnection({
|
||||||
const [serverImplementation, setServerImplementation] =
|
const [serverImplementation, setServerImplementation] =
|
||||||
useState<Implementation | null>(null);
|
useState<Implementation | null>(null);
|
||||||
|
|
||||||
|
type ReceiverTaskRecord = {
|
||||||
|
task: Task;
|
||||||
|
payloadPromise: Promise<ClientResult>;
|
||||||
|
resolvePayload: (payload: ClientResult) => void;
|
||||||
|
rejectPayload: (reason?: unknown) => void;
|
||||||
|
cleanupTimeoutId?: ReturnType<typeof setTimeout>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tasks created locally in response to *incoming* task-augmented requests
|
||||||
|
// (e.g. `sampling/createMessage` and `elicitation/create` with `params.task`).
|
||||||
|
const receiverTasksRef = useRef<Map<string, ReceiverTaskRecord>>(new Map());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!oauthClientId) {
|
if (!oauthClientId) {
|
||||||
clearClientInformationFromSessionStorage({
|
clearClientInformationFromSessionStorage({
|
||||||
|
|
@ -433,6 +456,22 @@ export function useConnection({
|
||||||
roots: {
|
roots: {
|
||||||
listChanged: true,
|
listChanged: true,
|
||||||
},
|
},
|
||||||
|
tasks: {
|
||||||
|
list: {},
|
||||||
|
cancel: {},
|
||||||
|
...(onPendingRequest || onElicitationRequest
|
||||||
|
? {
|
||||||
|
requests: {
|
||||||
|
...(onPendingRequest
|
||||||
|
? { sampling: { createMessage: {} } }
|
||||||
|
: undefined),
|
||||||
|
...(onElicitationRequest
|
||||||
|
? { elicitation: { create: {} } }
|
||||||
|
: undefined),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: undefined),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -714,6 +753,7 @@ export function useConnection({
|
||||||
ResourceListChangedNotificationSchema,
|
ResourceListChangedNotificationSchema,
|
||||||
ToolListChangedNotificationSchema,
|
ToolListChangedNotificationSchema,
|
||||||
PromptListChangedNotificationSchema,
|
PromptListChangedNotificationSchema,
|
||||||
|
TaskStatusNotificationSchema,
|
||||||
].forEach((notificationSchema) => {
|
].forEach((notificationSchema) => {
|
||||||
client.setNotificationHandler(notificationSchema, onNotification);
|
client.setNotificationHandler(notificationSchema, onNotification);
|
||||||
});
|
});
|
||||||
|
|
@ -793,11 +833,218 @@ export function useConnection({
|
||||||
setServerCapabilities(capabilities ?? null);
|
setServerCapabilities(capabilities ?? null);
|
||||||
setCompletionsSupported(capabilities?.completions !== undefined);
|
setCompletionsSupported(capabilities?.completions !== undefined);
|
||||||
|
|
||||||
|
const nowIso = () => new Date().toISOString();
|
||||||
|
|
||||||
|
const makeTaskId = () => {
|
||||||
|
// Prefer UUID when available; otherwise fall back to a reasonably unique id.
|
||||||
|
const cryptoAny = globalThis.crypto as unknown as
|
||||||
|
| { randomUUID?: () => string }
|
||||||
|
| undefined;
|
||||||
|
return (
|
||||||
|
cryptoAny?.randomUUID?.() ??
|
||||||
|
`task_${Date.now()}_${Math.random().toString(16).slice(2)}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const emitTaskStatus = async (task: Task) => {
|
||||||
|
// Best-effort; task status notifications are optional.
|
||||||
|
try {
|
||||||
|
const notification: ClientNotification = {
|
||||||
|
method: "notifications/tasks/status",
|
||||||
|
params: task,
|
||||||
|
} as unknown as ClientNotification;
|
||||||
|
await client.notification(notification);
|
||||||
|
pushHistory(notification);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Failed to send notifications/tasks/status", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const upsertReceiverTask = async (task: Task) => {
|
||||||
|
// Update task record and emit status notification.
|
||||||
|
const record = receiverTasksRef.current.get(task.taskId);
|
||||||
|
if (record) {
|
||||||
|
receiverTasksRef.current.set(task.taskId, { ...record, task });
|
||||||
|
}
|
||||||
|
await emitTaskStatus(task);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createReceiverTask = (opts: {
|
||||||
|
ttl?: number;
|
||||||
|
initialStatus: Task["status"];
|
||||||
|
statusMessage?: string;
|
||||||
|
pollInterval?: number;
|
||||||
|
}): ReceiverTaskRecord => {
|
||||||
|
const taskId = makeTaskId();
|
||||||
|
const createdAt = nowIso();
|
||||||
|
const ttl = opts.ttl ?? getMCPTaskTtl(config);
|
||||||
|
|
||||||
|
let resolvePayload: (payload: ClientResult) => void = () => undefined;
|
||||||
|
let rejectPayload: (reason?: unknown) => void = () => undefined;
|
||||||
|
const payloadPromise = new Promise<ClientResult>((resolve, reject) => {
|
||||||
|
resolvePayload = resolve;
|
||||||
|
rejectPayload = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
const task: Task = {
|
||||||
|
taskId,
|
||||||
|
status: opts.initialStatus,
|
||||||
|
ttl,
|
||||||
|
createdAt,
|
||||||
|
lastUpdatedAt: createdAt,
|
||||||
|
...(opts.pollInterval !== undefined
|
||||||
|
? { pollInterval: opts.pollInterval }
|
||||||
|
: undefined),
|
||||||
|
...(opts.statusMessage ? { statusMessage: opts.statusMessage } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const record: ReceiverTaskRecord = {
|
||||||
|
task,
|
||||||
|
payloadPromise,
|
||||||
|
resolvePayload,
|
||||||
|
rejectPayload,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cleanup after TTL (best-effort).
|
||||||
|
if (ttl !== null && ttl > 0) {
|
||||||
|
record.cleanupTimeoutId = setTimeout(() => {
|
||||||
|
receiverTasksRef.current.delete(taskId);
|
||||||
|
}, ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
receiverTasksRef.current.set(taskId, record);
|
||||||
|
void emitTaskStatus(task);
|
||||||
|
return record;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Server -> client Tasks handlers (receiver side)
|
||||||
|
client.setRequestHandler(ListTasksRequestSchema, async () => {
|
||||||
|
return {
|
||||||
|
tasks: Array.from(receiverTasksRef.current.values()).map(
|
||||||
|
(r) => r.task,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
client.setRequestHandler(GetTaskRequestSchema, async (request) => {
|
||||||
|
const record = receiverTasksRef.current.get(request.params.taskId);
|
||||||
|
if (!record) {
|
||||||
|
throw new McpError(
|
||||||
|
ErrorCode.InvalidParams,
|
||||||
|
`Unknown taskId: ${request.params.taskId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return record.task;
|
||||||
|
});
|
||||||
|
|
||||||
|
client.setRequestHandler(GetTaskPayloadRequestSchema, async (request) => {
|
||||||
|
const record = receiverTasksRef.current.get(request.params.taskId);
|
||||||
|
if (!record) {
|
||||||
|
throw new McpError(
|
||||||
|
ErrorCode.InvalidParams,
|
||||||
|
`Unknown taskId: ${request.params.taskId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block until the task payload is ready.
|
||||||
|
return await record.payloadPromise;
|
||||||
|
});
|
||||||
|
|
||||||
|
client.setRequestHandler(CancelTaskRequestSchema, async (request) => {
|
||||||
|
const record = receiverTasksRef.current.get(request.params.taskId);
|
||||||
|
if (!record) {
|
||||||
|
throw new McpError(
|
||||||
|
ErrorCode.InvalidParams,
|
||||||
|
`Unknown taskId: ${request.params.taskId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const terminalStatuses: Task["status"][] = [
|
||||||
|
"completed",
|
||||||
|
"failed",
|
||||||
|
"cancelled",
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!terminalStatuses.includes(record.task.status)) {
|
||||||
|
const updated: Task = {
|
||||||
|
...record.task,
|
||||||
|
status: "cancelled",
|
||||||
|
lastUpdatedAt: nowIso(),
|
||||||
|
statusMessage: "Cancelled",
|
||||||
|
};
|
||||||
|
receiverTasksRef.current.set(request.params.taskId, {
|
||||||
|
...record,
|
||||||
|
task: updated,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Unblock any pending `tasks/result`.
|
||||||
|
record.rejectPayload(
|
||||||
|
new McpError(ErrorCode.InternalError, "Task was cancelled"),
|
||||||
|
);
|
||||||
|
|
||||||
|
await emitTaskStatus(updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
return receiverTasksRef.current.get(request.params.taskId)!.task;
|
||||||
|
});
|
||||||
|
|
||||||
if (onPendingRequest) {
|
if (onPendingRequest) {
|
||||||
client.setRequestHandler(CreateMessageRequestSchema, (request) => {
|
client.setRequestHandler(CreateMessageRequestSchema, (request) => {
|
||||||
return new Promise((resolve, reject) => {
|
const taskSpec = (request as { params?: { task?: { ttl?: number } } })
|
||||||
onPendingRequest(request, resolve, reject);
|
.params?.task;
|
||||||
|
|
||||||
|
if (!taskSpec) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
onPendingRequest(request, resolve, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Task-augmented sampling request: return a task immediately and
|
||||||
|
// allow the server to poll via `tasks/get` and `tasks/result`.
|
||||||
|
const record = createReceiverTask({
|
||||||
|
ttl: taskSpec.ttl,
|
||||||
|
initialStatus: "input_required",
|
||||||
|
statusMessage: "Awaiting user input",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Background runner to complete and resolve this specific task record.
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
const payload = await new Promise((resolve, reject) => {
|
||||||
|
onPendingRequest(request, resolve, reject);
|
||||||
|
});
|
||||||
|
record.resolvePayload(payload as ClientResult);
|
||||||
|
const updated: Task = {
|
||||||
|
...record.task,
|
||||||
|
status: "completed",
|
||||||
|
lastUpdatedAt: nowIso(),
|
||||||
|
};
|
||||||
|
receiverTasksRef.current.set(record.task.taskId, {
|
||||||
|
...record,
|
||||||
|
task: updated,
|
||||||
|
});
|
||||||
|
await upsertReceiverTask(updated);
|
||||||
|
} catch (e) {
|
||||||
|
record.rejectPayload(e);
|
||||||
|
const updated: Task = {
|
||||||
|
...record.task,
|
||||||
|
status: "failed",
|
||||||
|
lastUpdatedAt: nowIso(),
|
||||||
|
statusMessage: e instanceof Error ? e.message : "Task failed",
|
||||||
|
};
|
||||||
|
receiverTasksRef.current.set(record.task.taskId, {
|
||||||
|
...record,
|
||||||
|
task: updated,
|
||||||
|
});
|
||||||
|
await upsertReceiverTask(updated);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
const createTaskResult: SchemaOutput<typeof CreateTaskResultSchema> =
|
||||||
|
{
|
||||||
|
task: record.task,
|
||||||
|
};
|
||||||
|
return createTaskResult;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -807,6 +1054,64 @@ export function useConnection({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (onElicitationRequest) {
|
||||||
|
client.setRequestHandler(ElicitRequestSchema, (request) => {
|
||||||
|
const taskSpec = (request as { params?: { task?: { ttl?: number } } })
|
||||||
|
.params?.task;
|
||||||
|
|
||||||
|
if (!taskSpec) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
onElicitationRequest(request, resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = createReceiverTask({
|
||||||
|
ttl: taskSpec.ttl,
|
||||||
|
initialStatus: "input_required",
|
||||||
|
statusMessage: "Awaiting user input",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run elicitation flow and resolve the task payload.
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
const payload = await new Promise((resolve) => {
|
||||||
|
onElicitationRequest(request, resolve);
|
||||||
|
});
|
||||||
|
record.resolvePayload(payload as ClientResult);
|
||||||
|
const updated: Task = {
|
||||||
|
...record.task,
|
||||||
|
status: "completed",
|
||||||
|
lastUpdatedAt: nowIso(),
|
||||||
|
};
|
||||||
|
receiverTasksRef.current.set(record.task.taskId, {
|
||||||
|
...record,
|
||||||
|
task: updated,
|
||||||
|
});
|
||||||
|
await upsertReceiverTask(updated);
|
||||||
|
} catch (e) {
|
||||||
|
record.rejectPayload(e);
|
||||||
|
const updated: Task = {
|
||||||
|
...record.task,
|
||||||
|
status: "failed",
|
||||||
|
lastUpdatedAt: nowIso(),
|
||||||
|
statusMessage: e instanceof Error ? e.message : "Task failed",
|
||||||
|
};
|
||||||
|
receiverTasksRef.current.set(record.task.taskId, {
|
||||||
|
...record,
|
||||||
|
task: updated,
|
||||||
|
});
|
||||||
|
await upsertReceiverTask(updated);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
const createTaskResult: SchemaOutput<typeof CreateTaskResultSchema> =
|
||||||
|
{
|
||||||
|
task: record.task,
|
||||||
|
};
|
||||||
|
return createTaskResult;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (capabilities?.logging && defaultLoggingLevel) {
|
if (capabilities?.logging && defaultLoggingLevel) {
|
||||||
lastRequest = "logging/setLevel";
|
lastRequest = "logging/setLevel";
|
||||||
await client.setLoggingLevel(defaultLoggingLevel);
|
await client.setLoggingLevel(defaultLoggingLevel);
|
||||||
|
|
@ -822,14 +1127,6 @@ export function useConnection({
|
||||||
lastRequest = "";
|
lastRequest = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onElicitationRequest) {
|
|
||||||
client.setRequestHandler(ElicitRequestSchema, async (request) => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
onElicitationRequest(request, resolve);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setMcpClient(client);
|
setMcpClient(client);
|
||||||
setConnectionStatus("connected");
|
setConnectionStatus("connected");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -855,7 +1152,35 @@ export function useConnection({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cancelTask = async (taskId: string) => {
|
||||||
|
return makeRequest(
|
||||||
|
{
|
||||||
|
method: "tasks/cancel",
|
||||||
|
params: { taskId },
|
||||||
|
},
|
||||||
|
CancelTaskResultSchema,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const listTasks = async (cursor?: string) => {
|
||||||
|
return makeRequest(
|
||||||
|
{
|
||||||
|
method: "tasks/list",
|
||||||
|
params: { cursor },
|
||||||
|
},
|
||||||
|
ListTasksResultSchema,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const disconnect = async () => {
|
const disconnect = async () => {
|
||||||
|
// Clear any receiver-side tasks + cleanup timers
|
||||||
|
receiverTasksRef.current.forEach((record) => {
|
||||||
|
if (record.cleanupTimeoutId) {
|
||||||
|
clearTimeout(record.cleanupTimeoutId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
receiverTasksRef.current.clear();
|
||||||
|
|
||||||
if (transportType === "streamable-http")
|
if (transportType === "streamable-http")
|
||||||
await (
|
await (
|
||||||
clientTransport as StreamableHTTPClientTransport
|
clientTransport as StreamableHTTPClientTransport
|
||||||
|
|
@ -885,6 +1210,8 @@ export function useConnection({
|
||||||
requestHistory,
|
requestHistory,
|
||||||
clearRequestHistory,
|
clearRequestHistory,
|
||||||
makeRequest,
|
makeRequest,
|
||||||
|
cancelTask,
|
||||||
|
listTasks,
|
||||||
sendNotification,
|
sendNotification,
|
||||||
handleCompletion,
|
handleCompletion,
|
||||||
completionsSupported,
|
completionsSupported,
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ export const getMCPProxyAuthToken = (
|
||||||
header: "X-MCP-Proxy-Auth",
|
header: "X-MCP-Proxy-Auth",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
export const getMCPTaskTtl = (config: InspectorConfig): number => {
|
||||||
|
return config.MCP_TASK_TTL.value as number;
|
||||||
|
};
|
||||||
|
|
||||||
export const getInitialTransportType = ():
|
export const getInitialTransportType = ():
|
||||||
| "stdio"
|
| "stdio"
|
||||||
|
|
|
||||||
107
package-lock.json
generated
107
package-lock.json
generated
|
|
@ -227,7 +227,6 @@
|
||||||
"integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
|
"integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@csstools/css-calc": "^2.1.3",
|
"@csstools/css-calc": "^2.1.3",
|
||||||
"@csstools/css-color-parser": "^3.0.9",
|
"@csstools/css-color-parser": "^3.0.9",
|
||||||
|
|
@ -241,8 +240,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
|
|
@ -275,6 +273,7 @@
|
||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
|
|
@ -820,7 +819,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT-0",
|
"license": "MIT-0",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
|
|
@ -841,7 +839,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
|
|
@ -866,7 +863,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@csstools/color-helpers": "^5.1.0",
|
"@csstools/color-helpers": "^5.1.0",
|
||||||
"@csstools/css-calc": "^2.1.4"
|
"@csstools/css-calc": "^2.1.4"
|
||||||
|
|
@ -1888,7 +1884,6 @@
|
||||||
"integrity": "sha512-kazxw2L9IPuZpQ0mEt9lu9Z98SqR74xcagANmMBU16X0lS23yPc0+S6hGLUz8kVRlomZEs/5S/Zlpqwf5yu6OQ==",
|
"integrity": "sha512-kazxw2L9IPuZpQ0mEt9lu9Z98SqR74xcagANmMBU16X0lS23yPc0+S6hGLUz8kVRlomZEs/5S/Zlpqwf5yu6OQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/environment": "30.2.0",
|
"@jest/environment": "30.2.0",
|
||||||
"@jest/fake-timers": "30.2.0",
|
"@jest/fake-timers": "30.2.0",
|
||||||
|
|
@ -1917,7 +1912,6 @@
|
||||||
"integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==",
|
"integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/fake-timers": "30.2.0",
|
"@jest/fake-timers": "30.2.0",
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
|
|
@ -1934,7 +1928,6 @@
|
||||||
"integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==",
|
"integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
"@sinonjs/fake-timers": "^13.0.0",
|
"@sinonjs/fake-timers": "^13.0.0",
|
||||||
|
|
@ -1953,7 +1946,6 @@
|
||||||
"integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==",
|
"integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sinclair/typebox": "^0.34.0"
|
"@sinclair/typebox": "^0.34.0"
|
||||||
},
|
},
|
||||||
|
|
@ -1967,7 +1959,6 @@
|
||||||
"integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==",
|
"integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/pattern": "30.0.1",
|
"@jest/pattern": "30.0.1",
|
||||||
"@jest/schemas": "30.0.5",
|
"@jest/schemas": "30.0.5",
|
||||||
|
|
@ -1986,8 +1977,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
|
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
|
||||||
"integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==",
|
"integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@jest/environment-jsdom-abstract/node_modules/@sinonjs/fake-timers": {
|
"node_modules/@jest/environment-jsdom-abstract/node_modules/@sinonjs/fake-timers": {
|
||||||
"version": "13.0.5",
|
"version": "13.0.5",
|
||||||
|
|
@ -1995,7 +1985,6 @@
|
||||||
"integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==",
|
"integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sinonjs/commons": "^3.0.1"
|
"@sinonjs/commons": "^3.0.1"
|
||||||
}
|
}
|
||||||
|
|
@ -2006,7 +1995,6 @@
|
||||||
"integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==",
|
"integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"@types/tough-cookie": "*",
|
"@types/tough-cookie": "*",
|
||||||
|
|
@ -2019,7 +2007,6 @@
|
||||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
|
|
@ -2039,7 +2026,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
|
@ -2050,7 +2036,6 @@
|
||||||
"integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==",
|
"integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
|
|
@ -2072,7 +2057,6 @@
|
||||||
"integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==",
|
"integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|
@ -2088,7 +2072,6 @@
|
||||||
"integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==",
|
"integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|
@ -2107,7 +2090,6 @@
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -2121,7 +2103,6 @@
|
||||||
"integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==",
|
"integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/schemas": "30.0.5",
|
"@jest/schemas": "30.0.5",
|
||||||
"ansi-styles": "^5.2.0",
|
"ansi-styles": "^5.2.0",
|
||||||
|
|
@ -2136,8 +2117,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@jest/expect": {
|
"node_modules/@jest/expect": {
|
||||||
"version": "29.7.0",
|
"version": "29.7.0",
|
||||||
|
|
@ -2206,7 +2186,6 @@
|
||||||
"integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==",
|
"integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"jest-regex-util": "30.0.1"
|
"jest-regex-util": "30.0.1"
|
||||||
|
|
@ -2221,7 +2200,6 @@
|
||||||
"integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==",
|
"integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
|
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -3967,8 +3945,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/babel__core": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
|
|
@ -4218,6 +4195,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz",
|
||||||
"integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==",
|
"integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
|
|
@ -4256,6 +4234,7 @@
|
||||||
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
|
|
@ -4267,6 +4246,7 @@
|
||||||
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
|
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^18.0.0"
|
"@types/react": "^18.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -4407,6 +4387,7 @@
|
||||||
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
|
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.49.0",
|
"@typescript-eslint/scope-manager": "8.49.0",
|
||||||
"@typescript-eslint/types": "8.49.0",
|
"@typescript-eslint/types": "8.49.0",
|
||||||
|
|
@ -4789,6 +4770,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
|
|
@ -5261,6 +5243,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
|
|
@ -6100,8 +6083,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/domexception": {
|
"node_modules/domexception": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
|
|
@ -6359,6 +6341,7 @@
|
||||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
|
|
@ -6656,6 +6639,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "^2.0.0",
|
"accepts": "^2.0.0",
|
||||||
"body-parser": "^2.2.1",
|
"body-parser": "^2.2.1",
|
||||||
|
|
@ -7749,6 +7733,7 @@
|
||||||
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/core": "^29.7.0",
|
"@jest/core": "^29.7.0",
|
||||||
"@jest/types": "^29.6.3",
|
"@jest/types": "^29.6.3",
|
||||||
|
|
@ -8089,7 +8074,6 @@
|
||||||
"integrity": "sha512-zbBTiqr2Vl78pKp/laGBREYzbZx9ZtqPjOK4++lL4BNDhxRnahg51HtoDrk9/VjIy9IthNEWdKVd7H5bqBhiWQ==",
|
"integrity": "sha512-zbBTiqr2Vl78pKp/laGBREYzbZx9ZtqPjOK4++lL4BNDhxRnahg51HtoDrk9/VjIy9IthNEWdKVd7H5bqBhiWQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/environment": "30.2.0",
|
"@jest/environment": "30.2.0",
|
||||||
"@jest/environment-jsdom-abstract": "30.2.0",
|
"@jest/environment-jsdom-abstract": "30.2.0",
|
||||||
|
|
@ -8115,7 +8099,6 @@
|
||||||
"integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==",
|
"integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/fake-timers": "30.2.0",
|
"@jest/fake-timers": "30.2.0",
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
|
|
@ -8132,7 +8115,6 @@
|
||||||
"integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==",
|
"integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
"@sinonjs/fake-timers": "^13.0.0",
|
"@sinonjs/fake-timers": "^13.0.0",
|
||||||
|
|
@ -8151,7 +8133,6 @@
|
||||||
"integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==",
|
"integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sinclair/typebox": "^0.34.0"
|
"@sinclair/typebox": "^0.34.0"
|
||||||
},
|
},
|
||||||
|
|
@ -8165,7 +8146,6 @@
|
||||||
"integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==",
|
"integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/pattern": "30.0.1",
|
"@jest/pattern": "30.0.1",
|
||||||
"@jest/schemas": "30.0.5",
|
"@jest/schemas": "30.0.5",
|
||||||
|
|
@ -8184,8 +8164,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
|
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
|
||||||
"integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==",
|
"integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/jest-environment-jsdom/node_modules/@sinonjs/fake-timers": {
|
"node_modules/jest-environment-jsdom/node_modules/@sinonjs/fake-timers": {
|
||||||
"version": "13.0.5",
|
"version": "13.0.5",
|
||||||
|
|
@ -8193,7 +8172,6 @@
|
||||||
"integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==",
|
"integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sinonjs/commons": "^3.0.1"
|
"@sinonjs/commons": "^3.0.1"
|
||||||
}
|
}
|
||||||
|
|
@ -8204,7 +8182,6 @@
|
||||||
"integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==",
|
"integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"@types/tough-cookie": "*",
|
"@types/tough-cookie": "*",
|
||||||
|
|
@ -8217,7 +8194,6 @@
|
||||||
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
|
|
@ -8228,7 +8204,6 @@
|
||||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
|
|
@ -8248,7 +8223,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
|
@ -8259,7 +8233,6 @@
|
||||||
"integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
|
"integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@asamuzakjp/css-color": "^3.2.0",
|
"@asamuzakjp/css-color": "^3.2.0",
|
||||||
"rrweb-cssom": "^0.8.0"
|
"rrweb-cssom": "^0.8.0"
|
||||||
|
|
@ -8274,7 +8247,6 @@
|
||||||
"integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
|
"integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"whatwg-mimetype": "^4.0.0",
|
"whatwg-mimetype": "^4.0.0",
|
||||||
"whatwg-url": "^14.0.0"
|
"whatwg-url": "^14.0.0"
|
||||||
|
|
@ -8289,7 +8261,6 @@
|
||||||
"integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
|
"integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"whatwg-encoding": "^3.1.1"
|
"whatwg-encoding": "^3.1.1"
|
||||||
},
|
},
|
||||||
|
|
@ -8303,7 +8274,6 @@
|
||||||
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agent-base": "^7.1.0",
|
"agent-base": "^7.1.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
|
|
@ -8318,7 +8288,6 @@
|
||||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agent-base": "^7.1.2",
|
"agent-base": "^7.1.2",
|
||||||
"debug": "4"
|
"debug": "4"
|
||||||
|
|
@ -8333,7 +8302,6 @@
|
||||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -8347,7 +8315,6 @@
|
||||||
"integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==",
|
"integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
|
|
@ -8369,7 +8336,6 @@
|
||||||
"integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==",
|
"integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|
@ -8385,7 +8351,6 @@
|
||||||
"integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==",
|
"integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|
@ -8404,7 +8369,6 @@
|
||||||
"integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
|
"integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssstyle": "^4.2.1",
|
"cssstyle": "^4.2.1",
|
||||||
"data-urls": "^5.0.0",
|
"data-urls": "^5.0.0",
|
||||||
|
|
@ -8445,7 +8409,6 @@
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -8459,7 +8422,6 @@
|
||||||
"integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==",
|
"integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/schemas": "30.0.5",
|
"@jest/schemas": "30.0.5",
|
||||||
"ansi-styles": "^5.2.0",
|
"ansi-styles": "^5.2.0",
|
||||||
|
|
@ -8474,8 +8436,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/jest-environment-jsdom/node_modules/tough-cookie": {
|
"node_modules/jest-environment-jsdom/node_modules/tough-cookie": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
|
|
@ -8483,7 +8444,6 @@
|
||||||
"integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
|
"integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tldts": "^6.1.32"
|
"tldts": "^6.1.32"
|
||||||
},
|
},
|
||||||
|
|
@ -8497,7 +8457,6 @@
|
||||||
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"punycode": "^2.3.1"
|
"punycode": "^2.3.1"
|
||||||
},
|
},
|
||||||
|
|
@ -8511,7 +8470,6 @@
|
||||||
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
|
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"xml-name-validator": "^5.0.0"
|
"xml-name-validator": "^5.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -8525,7 +8483,6 @@
|
||||||
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
|
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"iconv-lite": "0.6.3"
|
"iconv-lite": "0.6.3"
|
||||||
},
|
},
|
||||||
|
|
@ -8539,7 +8496,6 @@
|
||||||
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
|
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
|
|
@ -8550,7 +8506,6 @@
|
||||||
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tr46": "^5.1.0",
|
"tr46": "^5.1.0",
|
||||||
"webidl-conversions": "^7.0.0"
|
"webidl-conversions": "^7.0.0"
|
||||||
|
|
@ -8565,7 +8520,6 @@
|
||||||
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
|
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
|
|
@ -9160,6 +9114,7 @@
|
||||||
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
}
|
}
|
||||||
|
|
@ -9198,6 +9153,7 @@
|
||||||
"integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==",
|
"integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"abab": "^2.0.6",
|
"abab": "^2.0.6",
|
||||||
"acorn": "^8.8.1",
|
"acorn": "^8.8.1",
|
||||||
|
|
@ -9536,7 +9492,6 @@
|
||||||
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"lz-string": "bin/bin.js"
|
"lz-string": "bin/bin.js"
|
||||||
}
|
}
|
||||||
|
|
@ -10414,6 +10369,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
|
|
@ -10589,7 +10545,6 @@
|
||||||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1",
|
"ansi-regex": "^5.0.1",
|
||||||
"ansi-styles": "^5.0.0",
|
"ansi-styles": "^5.0.0",
|
||||||
|
|
@ -10605,7 +10560,6 @@
|
||||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
|
|
@ -10760,6 +10714,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
|
|
@ -10772,6 +10727,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"scheduler": "^0.23.2"
|
"scheduler": "^0.23.2"
|
||||||
|
|
@ -10785,8 +10741,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.18.0",
|
"version": "0.18.0",
|
||||||
|
|
@ -11194,8 +11149,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
|
||||||
"integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
|
"integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/run-applescript": {
|
"node_modules/run-applescript": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
|
|
@ -11861,6 +11815,7 @@
|
||||||
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
|
|
@ -11999,6 +11954,7 @@
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -12022,7 +11978,6 @@
|
||||||
"integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
|
"integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tldts-core": "^6.1.86"
|
"tldts-core": "^6.1.86"
|
||||||
},
|
},
|
||||||
|
|
@ -12035,8 +11990,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
|
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
|
||||||
"integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
|
"integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/tmpl": {
|
"node_modules/tmpl": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
|
|
@ -12209,6 +12163,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cspotcode/source-map-support": "^0.8.0",
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
"@tsconfig/node10": "^1.0.7",
|
"@tsconfig/node10": "^1.0.7",
|
||||||
|
|
@ -12265,6 +12220,7 @@
|
||||||
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "~0.27.0",
|
"esbuild": "~0.27.0",
|
||||||
"get-tsconfig": "^4.7.5"
|
"get-tsconfig": "^4.7.5"
|
||||||
|
|
@ -12818,6 +12774,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|
@ -13026,6 +12983,7 @@
|
||||||
"integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
|
"integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
|
|
@ -13119,6 +13077,7 @@
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -13538,6 +13497,7 @@
|
||||||
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
|
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"yaml": "bin.mjs"
|
"yaml": "bin.mjs"
|
||||||
},
|
},
|
||||||
|
|
@ -13625,6 +13585,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue