diff --git a/keep-ui/app/dashboard/EditGridItemModal.tsx b/keep-ui/app/dashboard/EditGridItemModal.tsx index 79e9b9ae1..35dfe709e 100644 --- a/keep-ui/app/dashboard/EditGridItemModal.tsx +++ b/keep-ui/app/dashboard/EditGridItemModal.tsx @@ -14,7 +14,7 @@ const EditGridItemModal: React.FC = ({ isOpen, onClose, const [thresholds, setThresholds] = useState([]); useEffect(() => { - if (item) { + if (item?.thresholds) { setThresholds(item.thresholds); } }, [item]); diff --git a/keep-ui/app/dashboard/GridItem.tsx b/keep-ui/app/dashboard/GridItem.tsx index a1ef8a5ea..6c85e2c43 100644 --- a/keep-ui/app/dashboard/GridItem.tsx +++ b/keep-ui/app/dashboard/GridItem.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; -import { Card } from "@tremor/react"; +import { AreaChart, Card } from "@tremor/react"; import MenuButton from "./MenuButton"; -import { WidgetData } from "./types"; +import {WidgetData, WidgetType} from "./types"; import AlertQuality from "@/app/alerts/alert-quality-table"; import { useSearchParams } from "next/navigation"; @@ -55,12 +55,14 @@ const GridItem: React.FC = ({ } const getColor = () => { let color = "#000000"; + if (item.widgetType === WidgetType.PRESET && item.thresholds && item.preset) { for (let i = item.thresholds.length - 1; i >= 0; i--) { if (item.preset && item.preset.alerts_count >= item.thresholds[i].value) { color = item.thresholds[i].color; break; } } + } return color; }; @@ -80,11 +82,13 @@ const GridItem: React.FC = ({ }; return ( - +
{/* For table view we need intract with table filter and pagination.so we aare dragging the widget here */} @@ -111,16 +115,40 @@ const GridItem: React.FC = ({
)} -
- -
- -
- ); -}; + { item.metric && ( +
+
+ + `${Intl.NumberFormat().format(number).toString()}` + } + startEndOnly + connectNulls + showLegend={false} + showTooltip={true} + xAxisLabel="Timestamp" + /> +
+
+ + )} + + +
+ +
+ +
+ ); + }; -export default GridItem; + export default GridItem; diff --git a/keep-ui/app/dashboard/GridLayout.tsx b/keep-ui/app/dashboard/GridLayout.tsx index 647fc3f3b..d78df13a3 100644 --- a/keep-ui/app/dashboard/GridLayout.tsx +++ b/keep-ui/app/dashboard/GridLayout.tsx @@ -4,6 +4,7 @@ import GridItemContainer from "./GridItemContainer"; import { LayoutItem, WidgetData } from "./types"; import "react-grid-layout/css/styles.css"; import { Preset } from "app/alerts/models"; +import {MetricsWidget} from "@/utils/hooks/useDashboardMetricWidgets"; const ResponsiveGridLayout = WidthProvider(Responsive); @@ -15,6 +16,7 @@ interface GridLayoutProps { onDelete: (id: string) => void; presets: Preset[]; onSave: (updateItem: WidgetData) => void; + metrics: MetricsWidget[]; } const GridLayout: React.FC = ({ @@ -25,6 +27,7 @@ const GridLayout: React.FC = ({ onDelete, onSave, presets, + metrics }) => { const layouts = { lg: layout }; @@ -52,14 +55,18 @@ const GridLayout: React.FC = ({ draggableHandle=".grid-item__widget" > {data.map((item) => { - //Fixing the static hardcode db value. + //Updating the static hardcode db value. if (item.preset) { const preset = presets?.find((p) => p?.id === item?.preset?.id); item.preset = { ...item.preset, alerts_count: preset?.alerts_count ?? 0, }; - + } else if (item.metric) { + const metric = metrics?.find(m => m?.id === item?.metric?.id); + if (metric) { + item.metric = {...metric} + } } return (
diff --git a/keep-ui/app/dashboard/WidgetModal.tsx b/keep-ui/app/dashboard/WidgetModal.tsx index a264c869a..ff331379f 100644 --- a/keep-ui/app/dashboard/WidgetModal.tsx +++ b/keep-ui/app/dashboard/WidgetModal.tsx @@ -1,32 +1,30 @@ -import React, { useState, useEffect, ChangeEvent, FormEvent } from "react"; +import React, {ChangeEvent, useEffect, useState} from "react"; import Modal from "@/components/ui/Modal"; -import { Button, Subtitle, TextInput, Select, SelectItem, Icon } from "@tremor/react"; -import { Trashcan } from "components/icons"; -import { Threshold, WidgetData, GenericsMertics } from "./types"; -import { Preset } from "app/alerts/models"; -import { useForm, Controller, get } from "react-hook-form"; +import {Button, Icon, Select, SelectItem, Subtitle, TextInput} from "@tremor/react"; +import {Trashcan} from "components/icons"; +import {GenericsMetrics, Threshold, WidgetData, WidgetType} from "./types"; +import {Preset} from "app/alerts/models"; +import {Controller, get, useForm, useWatch} from "react-hook-form"; +import {MetricsWidget} from "@/utils/hooks/useDashboardMetricWidgets"; interface WidgetForm { widgetName: string; selectedPreset: string; thresholds: Threshold[]; - selectedWidgetType: string; + widgetType: WidgetType; + selectedMetricWidget: string; selectedGenericMetrics: string; } interface WidgetModalProps { isOpen: boolean; onClose: () => void; - onAddWidget: ( - preset: Preset | null, - thresholds: Threshold[], - name: string, - widgetType?: string, - genericMetrics?: GenericsMertics | null, + onAddWidget: (name: string, widgetType: WidgetType, preset?: Preset, thresholds?: Threshold[], metric?: MetricsWidget, genericMetrics?: GenericsMetrics, ) => void; onEditWidget: (updatedWidget: WidgetData) => void; presets: Preset[]; editingItem?: WidgetData | null; + metricWidgets: MetricsWidget[]; } const GENERIC_METRICS = [ @@ -35,85 +33,100 @@ const GENERIC_METRICS = [ label: "Alert Quality", widgetType: "table", meta: { - defaultFilters: {"fields":"severity"}, + defaultFilters: {"fields": "severity"}, }, }, -] as GenericsMertics[]; +] as GenericsMetrics[]; -const WidgetModal: React.FC = ({ isOpen, onClose, onAddWidget, onEditWidget, presets, editingItem }) => { +const WidgetModal: React.FC = ({ + isOpen, + onClose, + onAddWidget, + onEditWidget, + presets, + editingItem, + metricWidgets + }) => { const [thresholds, setThresholds] = useState([ - { value: 0, color: '#22c55e' }, // Green - { value: 20, color: '#ef4444' } // Red + {value: 0, color: '#22c55e'}, // Green + {value: 20, color: '#ef4444'} // Red ]); - const { control, handleSubmit, setValue, formState: { errors }, reset } = useForm({ + const {control, handleSubmit, setValue, formState: {errors}, reset} = useForm({ defaultValues: { widgetName: '', selectedPreset: '', thresholds: thresholds, - selectedWidgetType: '', + widgetType: WidgetType.PRESET, selectedGenericMetrics: '' } }); - const [currentWidgetType, setCurrentWidgetType] = useState(''); + const widgetType = useWatch({ + control, + name: 'widgetType', + }); useEffect(() => { if (editingItem) { setValue("widgetName", editingItem.name); + setValue('widgetType', editingItem.widgetType); + + if (editingItem.thresholds) { + setThresholds(editingItem.thresholds); + } setValue("selectedPreset", editingItem?.preset?.id ?? ""); - setValue("selectedWidgetType", editingItem?.widgetType ?? ""); + setValue('selectedMetricWidget', editingItem?.metric?.id ?? ""); setValue("selectedGenericMetrics", editingItem?.genericMetrics?.key ?? ""); - setThresholds(editingItem.thresholds); } else { reset({ widgetName: '', selectedPreset: '', + selectedMetricWidget: '', + selectedGenericMetrics: '', thresholds: thresholds, - selectedWidgetType: "", + widgetType: WidgetType.PRESET, }); } }, [editingItem, setValue, reset]); const handleThresholdChange = (index: number, field: 'value' | 'color', e: ChangeEvent) => { const value = field === 'value' ? e.target.value : e.target.value; - const updatedThresholds = thresholds.map((t, i) => i === index ? { ...t, [field]: value } : t); + const updatedThresholds = thresholds.map((t, i) => i === index ? {...t, [field]: value} : t); setThresholds(updatedThresholds); }; const handleThresholdBlur = () => { setThresholds(prevThresholds => { return prevThresholds - .map(t => ({ - ...t, - value: parseInt(t.value.toString(), 10) || 0 - })) - .sort((a, b) => a.value - b.value); + .map(t => ({ + ...t, + value: parseInt(t.value.toString(), 10) || 0 + })) + .sort((a, b) => a.value - b.value); }); }; const handleAddThreshold = () => { const maxThreshold = Math.max(...thresholds.map(t => t.value), 0); - setThresholds([...thresholds, { value: maxThreshold + 10, color: '#000000' }]); + setThresholds([...thresholds, {value: maxThreshold + 10, color: '#000000'}]); }; const handleRemoveThreshold = (index: number) => { setThresholds(thresholds.filter((_, i) => i !== index)); }; - const deepClone = (obj: GenericsMertics|undefined) => { - if(!obj){ - return null; - } - try{ - return JSON.parse(JSON.stringify(obj)) as GenericsMertics; - }catch(e){ - return null; + const deepClone = (obj: GenericsMetrics | undefined) => { + if (!obj) { + return obj; } + return JSON.parse(JSON.stringify(obj)) as GenericsMetrics; + }; const onSubmit = (data: WidgetForm) => { - const preset = presets.find(p => p.id === data.selectedPreset) ?? null; + const preset = presets.find(p => p.id === data.selectedPreset); + const metric = metricWidgets.find(p => p.id === data.selectedMetricWidget); if (preset || data.selectedGenericMetrics) { const formattedThresholds = thresholds.map(t => ({ ...t, @@ -124,31 +137,51 @@ const WidgetModal: React.FC = ({ isOpen, onClose, onAddWidget, let updatedWidget: WidgetData = { ...editingItem, name: data.widgetName, - widgetType: data.selectedWidgetType || "preset", //backwards compatibility + widgetType: data.widgetType || WidgetType.PRESET, // backwards compatibility preset, thresholds: formattedThresholds, - genericMetrics: editingItem.genericMetrics || null, + genericMetrics: editingItem.genericMetrics, }; onEditWidget(updatedWidget); } else { onAddWidget( - preset, - formattedThresholds, - data.widgetName, - data.selectedWidgetType, - deepClone(GENERIC_METRICS.find((g) => g.key === data.selectedGenericMetrics)) + data.widgetName, + data.widgetType, + preset, + formattedThresholds, + undefined, + deepClone(GENERIC_METRICS.find((g) => g.key === data.selectedGenericMetrics)) ); // cleanup form setThresholds([ - { value: 0, color: '#22c55e' }, // Green - { value: 20, color: '#ef4444' } // Red + {value: 0, color: '#22c55e'}, // Green + {value: 20, color: '#ef4444'} // Red ]); reset({ widgetName: '', selectedPreset: '', thresholds: thresholds, selectedGenericMetrics: '', - selectedWidgetType: '', + widgetType: WidgetType.PRESET, + }); + } + onClose(); + } + if (metric) { + if (editingItem) { + const updatedWidget: WidgetData = { + ...editingItem, + name: data.widgetName, + widgetType: data.widgetType, + }; + onEditWidget(updatedWidget); + } else { + onAddWidget(data.widgetName, data.widgetType, undefined, undefined, metric, undefined); + reset({ + widgetName: '', + selectedPreset: '', + widgetType: WidgetType.PRESET, + thresholds: thresholds, }); } onClose(); @@ -156,135 +189,156 @@ const WidgetModal: React.FC = ({ isOpen, onClose, onAddWidget, }; return ( - -
-
- Widget Name - ( - - )} - /> -
-
- Widget Type - { - setCurrentWidgetType(field.value); - return - }} - /> -
- {currentWidgetType === 'preset' ? ( - <> -
- Preset - ( - - )} - /> -
-
-
- Thresholds - -
-
- {thresholds.map((threshold, index) => ( -
- handleThresholdChange(index, 'value', e)} - onBlur={handleThresholdBlur} - placeholder="Threshold value" - required - /> - handleThresholdChange(index, 'color', e)} - className="w-10 h-10 p-1 border" - required - /> - {thresholds.length > 1 && ( - + + +
+ Widget Name + ( + )} -
- ))} + />
-
- - ): currentWidgetType === 'generic_metrics' && <>
- Generic Metrics - ( - - )} - /> + Widget Type + { + return + }} + />
- } - - - + {widgetType === WidgetType.PRESET ? ( + <> +
+ Preset + ( + + )} + /> +
+
+
+ Thresholds + +
+
+ {thresholds.map((threshold, index) => ( +
+ handleThresholdChange(index, 'value', e)} + onBlur={handleThresholdBlur} + placeholder="Threshold value" + required + /> + handleThresholdChange(index, 'color', e)} + className="w-10 h-10 p-1 border" + required + /> + {thresholds.length > 1 && ( + + )} +
+ ))} +
+
+ + ) : widgetType === WidgetType.GENERICS_METRICS ? <> +
+ Generic Metrics + ( + + )} + /> +
+ : +
+ Widget + ( + + )} + /> +
} + + + ); }; diff --git a/keep-ui/app/dashboard/[id]/dashboard.tsx b/keep-ui/app/dashboard/[id]/dashboard.tsx index fef94ac8c..77b060624 100644 --- a/keep-ui/app/dashboard/[id]/dashboard.tsx +++ b/keep-ui/app/dashboard/[id]/dashboard.tsx @@ -1,20 +1,20 @@ "use client"; -import { useParams } from "next/navigation"; -import { useState, useEffect, ChangeEvent } from "react"; +import {useParams} from "next/navigation"; +import {ChangeEvent, useEffect, useState} from "react"; import GridLayout from "../GridLayout"; -import { usePresets } from "utils/hooks/usePresets"; import WidgetModal from "../WidgetModal"; -import { Button, Card, TextInput, Subtitle, Icon } from "@tremor/react"; -import { LayoutItem, WidgetData, Threshold, GenericsMertics } from "../types"; -import { Preset } from "app/alerts/models"; -import { FiSave, FiEdit2 } from "react-icons/fi"; -import { useSession } from "next-auth/react"; -import { useDashboards } from "utils/hooks/useDashboards"; -import { useApiUrl } from "utils/hooks/useConfig"; +import {Button, Card, Icon, Subtitle, TextInput} from "@tremor/react"; +import {GenericsMetrics, LayoutItem, Threshold, WidgetData, WidgetType} from "../types"; +import {Preset} from "app/alerts/models"; +import {FiEdit2, FiSave} from "react-icons/fi"; +import {useSession} from "next-auth/react"; +import {useDashboards} from "utils/hooks/useDashboards"; +import {useApiUrl} from "utils/hooks/useConfig"; import "./../styles.css"; -import { toast } from "react-toastify"; -import { GenericFilters } from "@/components/filters/GenericFilters"; -import { useDashboardPreset } from "utils/hooks/useDashboardPresets"; +import {toast} from "react-toastify"; +import {GenericFilters} from "@/components/filters/GenericFilters"; +import {useDashboardPreset} from "utils/hooks/useDashboardPresets"; +import {MetricsWidget, useDashboardMetricWidgets} from '@/utils/hooks/useDashboardMetricWidgets'; const DASHBOARD_FILTERS = [ { @@ -33,6 +33,7 @@ const DashboardPage = () => { const [isModalOpen, setIsModalOpen] = useState(false); const [layout, setLayout] = useState([]); const [widgetData, setWidgetData] = useState([]); + const {widgets: allMetricWidgets} = useDashboardMetricWidgets(true); const [editingItem, setEditingItem] = useState(null); const [dashboardName, setDashboardName] = useState(decodeURIComponent(id)); const [isEditingName, setIsEditingName] = useState(false); @@ -58,21 +59,20 @@ const DashboardPage = () => { const closeModal = () => setIsModalOpen(false); const handleAddWidget = ( - preset: Preset | null, - thresholds: Threshold[], - name: string, - widgetType?: string, - genericMetrics?: GenericsMertics | null + name: string, widgetType: WidgetType, preset?: Preset , + thresholds?: Threshold[], + metric?: MetricsWidget, + genericMetrics?: GenericsMetrics ) => { const uniqueId = `w-${Date.now()}`; const newItem: LayoutItem = { i: uniqueId, x: (layout.length % 12) * 2, y: Math.floor(layout.length / 12) * 2, - w: genericMetrics ? 12 : 3, - h: genericMetrics ? 20 : 3, - minW: genericMetrics ? 10 : 2, - minH: genericMetrics ? 15 : 2, + w: widgetType === WidgetType.GENERICS_METRICS ? 12 : widgetType === WidgetType.METRIC ? 6 : 3, + h: widgetType === WidgetType.GENERICS_METRICS ? 20 : widgetType === WidgetType.METRIC ? 8 : 3, + minW: widgetType === WidgetType.GENERICS_METRICS ? 10 : 2, + minH: widgetType === WidgetType.GENERICS_METRICS ? 15 : widgetType === WidgetType.METRIC ? 7 : 3, static: false, }; const newWidget: WidgetData = { @@ -80,8 +80,9 @@ const DashboardPage = () => { thresholds, preset, name, - widgetType: widgetType || "preset", - genericMetrics: genericMetrics || null, + widgetType, + genericMetrics, + metric }; setLayout((prevLayout) => [...prevLayout, newItem]); setWidgetData((prevData) => [...prevData, newWidget]); @@ -89,6 +90,7 @@ const DashboardPage = () => { const handleEditWidget = (id: string, update?: WidgetData) => { let itemToEdit = widgetData.find((d) => d.i === id) || null; + console.log(itemToEdit, update) if (itemToEdit && update) { setEditingItem({ ...itemToEdit, ...update }); } else { @@ -226,6 +228,7 @@ const DashboardPage = () => { onDelete={handleDeleteWidget} onSave={handleSaveEdit} presets={allPresets} + metrics={allMetricWidgets} /> )} @@ -236,6 +239,7 @@ const DashboardPage = () => { onEditWidget={handleSaveEdit} presets={allPresets} editingItem={editingItem} + metricWidgets={allMetricWidgets} />
); diff --git a/keep-ui/app/dashboard/types.tsx b/keep-ui/app/dashboard/types.tsx index 05ca0cd95..be80ac842 100644 --- a/keep-ui/app/dashboard/types.tsx +++ b/keep-ui/app/dashboard/types.tsx @@ -1,35 +1,44 @@ -import { Preset } from "app/alerts/models"; +import {Preset} from "app/alerts/models"; +import {MetricsWidget} from "@/utils/hooks/useDashboardMetricWidgets"; + export interface LayoutItem { - i: string; - x: number; - y: number; - w: number; - h: number; - minW?: number; - minH?: number; - static: boolean; - } + i: string; + x: number; + y: number; + w: number; + h: number; + minW?: number; + minH?: number; + static: boolean; +} - export interface GenericsMertics { - key: string; - label: string; - widgetType: "table" | "chart"; - meta: { - defaultFilters: { - [key: string]: string|string[]; - }, - } +export interface GenericsMetrics { + key: string; + label: string; + widgetType: "table" | "chart"; + meta: { + defaultFilters: { + [key: string]: string | string[]; + }, } +} - export interface WidgetData extends LayoutItem { - thresholds: Threshold[]; - preset: Preset | null; - name: string; - widgetType?:string; - genericMetrics?: GenericsMertics| null; - } +export enum WidgetType { + PRESET = 'PRESET', + METRIC = 'METRIC', + GENERICS_METRICS = 'GENERICS_METRICS' +} - export interface Threshold { - value: number; - color: string; - } +export interface WidgetData extends LayoutItem { + thresholds?: Threshold[]; + preset?: Preset; + name: string; + widgetType: WidgetType; + genericMetrics?: GenericsMetrics; + metric?: MetricsWidget; +} + +export interface Threshold { + value: number; + color: string; +} diff --git a/keep-ui/app/frigade-provider.tsx b/keep-ui/app/frigade-provider.tsx new file mode 100644 index 000000000..3d98f4aa3 --- /dev/null +++ b/keep-ui/app/frigade-provider.tsx @@ -0,0 +1,32 @@ +"use client"; + +import * as Frigade from "@frigade/react"; +import { useSession } from "next-auth/react"; +export const FrigadeProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const { data: session } = useSession(); + return ( + + {children} + + ); +}; diff --git a/keep-ui/app/layout.tsx b/keep-ui/app/layout.tsx index 89f66807f..a8a921d2f 100644 --- a/keep-ui/app/layout.tsx +++ b/keep-ui/app/layout.tsx @@ -14,6 +14,7 @@ const mulish = Mulish({ import { ToastContainer } from "react-toastify"; import Navbar from "components/navbar/Navbar"; import { TopologyPollingContextProvider } from "@/app/topology/model/TopologyPollingContext"; +import { FrigadeProvider } from "./frigade-provider"; type RootLayoutProps = { children: ReactNode; @@ -25,13 +26,15 @@ export default async function RootLayout({ children }: RootLayoutProps) { - {/* @ts-ignore-error Server Component */} - - {/* https://discord.com/channels/752553802359505017/1068089513253019688/1117731746922893333 */} -
-
{children}
- -
+ + {/* @ts-ignore-error Server Component */} + + {/* https://discord.com/channels/752553802359505017/1068089513253019688/1117731746922893333 */} +
+
{children}
+ +
+
diff --git a/keep-ui/app/settings/webhook-settings.tsx b/keep-ui/app/settings/webhook-settings.tsx index 8b3c5708c..10d6dda67 100644 --- a/keep-ui/app/settings/webhook-settings.tsx +++ b/keep-ui/app/settings/webhook-settings.tsx @@ -23,6 +23,7 @@ import { fetcher } from "utils/fetcher"; import { toast } from "react-toastify"; import { v4 as uuidv4 } from "uuid"; import { ExclamationCircleIcon } from "@heroicons/react/24/outline"; +import * as Frigade from "@frigade/react"; interface Webhook { webhookApi: string; @@ -181,9 +182,15 @@ req.end(); URL: {data.webhookApi} API Key: {data.apiKey}
- +
- + diff --git a/keep-ui/components/navbar/Onboarding.tsx b/keep-ui/components/navbar/Onboarding.tsx new file mode 100644 index 000000000..47054c91a --- /dev/null +++ b/keep-ui/components/navbar/Onboarding.tsx @@ -0,0 +1,52 @@ +import { Fragment } from "react"; +import { Dialog, Transition } from "@headlessui/react"; +import { Badge, Button, Title } from "@tremor/react"; +import { IoMdClose } from "react-icons/io"; +import * as Frigade from "@frigade/react"; + +interface OnboardingProps { + isOpen: boolean; + toggle: () => void; + variables?: Record; +} + +export default function Onboarding({ + isOpen, + toggle, + variables, +}: OnboardingProps) { + return ( + + + + + + ); +} diff --git a/keep-ui/components/navbar/UserInfo.tsx b/keep-ui/components/navbar/UserInfo.tsx index e848579a3..a7c9ccd41 100644 --- a/keep-ui/components/navbar/UserInfo.tsx +++ b/keep-ui/components/navbar/UserInfo.tsx @@ -6,7 +6,6 @@ import { Session } from "next-auth"; import { signOut } from "next-auth/react"; import { useConfig } from "utils/hooks/useConfig"; import { AuthenticationType } from "utils/authenticationType"; -import Image from "next/image"; import Link from "next/link"; import { LuSlack } from "react-icons/lu"; import { AiOutlineRight } from "react-icons/ai"; @@ -15,6 +14,9 @@ import DarkModeToggle from "app/dark-mode-toggle"; import { useFloating } from "@floating-ui/react"; import { Icon, Subtitle } from "@tremor/react"; import UserAvatar from "./UserAvatar"; +import * as Frigade from "@frigade/react"; +import { useState } from "react"; +import Onboarding from "./Onboarding"; type UserDropdownProps = { session: Session; @@ -85,28 +87,49 @@ type UserInfoProps = { }; export const UserInfo = ({ session }: UserInfoProps) => { - return ( -
    -
  • - - Providers - -
  • -
  • - {/* TODO: slows everything down. needs to be replaced */} - -
  • -
  • - - Join our Slack - -
  • + const [isOnboardingComplete, setIsOnboardingComplete] = useState(false); + const [isOnboardingOpen, setIsOnboardingOpen] = useState(false); - {session && } -
+ return ( + <> +
    +
  • + + Providers + +
  • +
  • + {/* TODO: slows everything down. needs to be replaced */} + +
  • +
  • + + Join our Slack + +
  • + {session && } + {isOnboardingComplete === false && ( +
  • + setIsOnboardingComplete(true)} + onClick={() => setIsOnboardingOpen(true)} + // css={{ backgroundColor: "#F9FAFB" }} + /> + setIsOnboardingOpen(false)} + variables={{ + name: session?.user.name ?? session?.user.email, + }} + /> +
  • + )} +
+ ); }; diff --git a/keep-ui/package-lock.json b/keep-ui/package-lock.json index 51d1bda23..334419990 100644 --- a/keep-ui/package-lock.json +++ b/keep-ui/package-lock.json @@ -10,9 +10,9 @@ "license": "ISC", "dependencies": { "@boiseitguru/cookie-cutter": "^0.2.3", - "@copilotkit/react-core": "^1.3.2", - "@copilotkit/react-textarea": "^1.3.2", - "@copilotkit/react-ui": "^1.3.2", + "@copilotkit/react-core": "^1.3.7", + "@copilotkit/react-textarea": "^1.3.7", + "@copilotkit/react-ui": "^1.3.7", "@dagrejs/dagre": "^1.1.3", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", @@ -24,6 +24,7 @@ "@fortawesome/free-brands-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", + "@frigade/react": "^2.5.26", "@headlessui/react": "^1.7.14", "@heroicons/react": "^2.1.5", "@mui/material": "^5.15.18", @@ -273,7 +274,7 @@ "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.12", "postcss-value-parser": "^4.2.0", - "posthog-js": "^1.168.0", + "posthog-js": "^1.178.0", "posthog-node": "^3.1.1", "preact-render-to-string": "^5.2.6", "prelude-ls": "^1.2.1", @@ -398,9 +399,9 @@ } }, "node_modules/@0no-co/graphql.web": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.8.tgz", - "integrity": "sha512-8BG6woLtDMvXB9Ajb/uE+Zr/U7y4qJ3upXi0JQHZmsKUJa7HjF/gFvmL2f3/mSmfZoQGRr9VoY97LCX2uaFMzA==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.9.tgz", + "integrity": "sha512-lXSg4bDHvP8CiMdpQf9f/rca12IIjXHN/p0Rc5mgzgLe4JBlIoA1zFa9NKhfG1bW0OyI2hgaOldFCfkEQwZuEQ==", "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" }, @@ -456,13 +457,23 @@ } }, "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { - "version": "18.19.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz", - "integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==", + "version": "18.19.62", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.62.tgz", + "integrity": "sha512-UOGhw+yZV/icyM0qohQVh3ktpY40Sp7tdTW7HxG3pTd7AiMrlFlAJNUrGK9t5mdW0+ViQcFV74zCSIx9ZJpncA==", "dependencies": { "undici-types": "~5.26.4" } }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-2.0.2.tgz", + "integrity": "sha512-x1KXOatwofR6ZAYzXRBL5wrdV0vwNxlTCK9NCuLqAzQYARqGcvFwiJA6A1ERuh+dgeA4Dxm3JBYictIes+SqUQ==", + "dependencies": { + "bidi-js": "^1.0.3", + "css-tree": "^2.3.1", + "is-potential-custom-element-name": "^1.0.1" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -2263,28 +2274,28 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "node_modules/@copilotkit/react-core": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@copilotkit/react-core/-/react-core-1.3.2.tgz", - "integrity": "sha512-MFmDYP1inMREGlJ7P1TclAiKILR254nSollYNO0zn4VyPEh9FYoAsMEnkgdx7i6jEUoKgYdP60t8x/fTSgpIEw==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@copilotkit/react-core/-/react-core-1.3.7.tgz", + "integrity": "sha512-2Xko0EiFsIKRmk7pz/mWpwA9qwXzmCsO8aJfkhKw6N2qOox4K1kvOpBSuJTjDR24dg+znoEBDGp6NwYbWokBiw==", "dependencies": { - "@copilotkit/runtime-client-gql": "1.3.2", - "@copilotkit/shared": "1.3.2", + "@copilotkit/runtime-client-gql": "1.3.7", + "@copilotkit/shared": "1.3.7", "@scarf/scarf": "^1.3.0", "untruncate-json": "^0.0.1" }, "peerDependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "node_modules/@copilotkit/react-textarea": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@copilotkit/react-textarea/-/react-textarea-1.3.2.tgz", - "integrity": "sha512-0IHUkzsp7Gu7c3PUZYDrrBWPYLF8c4erAZgkqIlVmBqbtuB1ZnKsDMHL3TnVashyoFLXT/uuok0QO/CH401/Vg==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@copilotkit/react-textarea/-/react-textarea-1.3.7.tgz", + "integrity": "sha512-Ak1+6qo+YuiA4Zw6LnVorOK2/dtYsuTaE5xZDCo8n/CXMdAwDuj3aFMC8ayAuuwKZFh9xdeHUuPAaNmRUBX3HQ==", "dependencies": { - "@copilotkit/react-core": "1.3.2", - "@copilotkit/runtime-client-gql": "1.3.2", - "@copilotkit/shared": "1.3.2", + "@copilotkit/react-core": "1.3.7", + "@copilotkit/runtime-client-gql": "1.3.7", + "@copilotkit/shared": "1.3.7", "@emotion/css": "^11.11.2", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", @@ -2306,7 +2317,7 @@ "tailwind-merge": "^1.13.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18 || ^19 || ^19.0.0-rc" } }, "node_modules/@copilotkit/react-textarea/node_modules/clsx": { @@ -2318,13 +2329,13 @@ } }, "node_modules/@copilotkit/react-ui": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@copilotkit/react-ui/-/react-ui-1.3.2.tgz", - "integrity": "sha512-Fun9UJyQFI45YU0TVZPPdq9XgtuGYb2NQ+LS0iMONGEd6mTLcaoxhusls8rFsmYx5DwdMVeg5VPHaSBXnW8Fng==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@copilotkit/react-ui/-/react-ui-1.3.7.tgz", + "integrity": "sha512-BWe9XXOjgTlJOqIXGEUad4eCD4m2skJJ4N6YxkHDhlhJx9pkHfD7CR4WqBuT0RIF3RUymHQxhTQ3AGWm739ccA==", "dependencies": { - "@copilotkit/react-core": "1.3.2", - "@copilotkit/runtime-client-gql": "1.3.2", - "@copilotkit/shared": "1.3.2", + "@copilotkit/react-core": "1.3.7", + "@copilotkit/runtime-client-gql": "1.3.7", + "@copilotkit/shared": "1.3.7", "@headlessui/react": "^2.1.3", "react-markdown": "^8.0.7", "react-syntax-highlighter": "^15.5.0", @@ -2332,7 +2343,7 @@ "remark-math": "^5.1.1" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18 || ^19 || ^19.0.0-rc" } }, "node_modules/@copilotkit/react-ui/node_modules/@headlessui/react": { @@ -3320,16 +3331,16 @@ } }, "node_modules/@copilotkit/runtime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@copilotkit/runtime/-/runtime-1.3.2.tgz", - "integrity": "sha512-7fYqKoxgPlzbOOQL3ZdlFtaCgxYI2Zkr+CZ6t1JpUf/3OCTi7j/3X/aLR643MEX6LB+DfDo4T3eQDJmW/WMqNQ==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@copilotkit/runtime/-/runtime-1.3.7.tgz", + "integrity": "sha512-L9GtUogvjFhDsNoPJkfgXaH1fwztbZkKkUVHeNcP9Wxg5MHeuZKWTQERiUAT2zzXCdIUkkWQlRDN05Q5fv1J+Q==", "dependencies": { "@anthropic-ai/sdk": "^0.27.3", - "@copilotkit/shared": "1.3.2", - "@google/generative-ai": "^0.11.2", + "@copilotkit/shared": "1.3.7", "@graphql-yoga/plugin-defer-stream": "^3.3.1", "@langchain/community": "^0.0.53", - "@langchain/core": "^0.1.61", + "@langchain/core": "^0.3.13", + "@langchain/google-gauth": "^0.1.0", "@langchain/openai": "^0.0.28", "class-transformer": "^0.5.1", "express": "^4.19.2", @@ -3337,7 +3348,7 @@ "graphql-scalars": "^1.23.0", "graphql-yoga": "^5.3.1", "groq-sdk": "^0.5.0", - "langchain": "^0.1.36", + "langchain": "^0.3.3", "openai": "^4.50.0", "pino": "^9.2.0", "pino-pretty": "^11.2.1", @@ -3348,12 +3359,12 @@ } }, "node_modules/@copilotkit/runtime-client-gql": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@copilotkit/runtime-client-gql/-/runtime-client-gql-1.3.2.tgz", - "integrity": "sha512-b7CbLrx8fNUbRsK3wVyiWGCng3XuejBFg1goP4oWs0xC968Zq1GJIXgTalYNQh0A1wc8AGY+4qm0O047ZarBqw==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@copilotkit/runtime-client-gql/-/runtime-client-gql-1.3.7.tgz", + "integrity": "sha512-JuwuDqP4eo3vToaln4/E9H0G8UjFNXT0/zn7t1z8jQXk06/7/Qh3zZSzbNJSfZ/0Mx9vBVrfMo+KCere8dgRIA==", "dependencies": { - "@copilotkit/runtime": "1.3.2", - "@copilotkit/shared": "1.3.2", + "@copilotkit/runtime": "1.3.7", + "@copilotkit/shared": "1.3.7", "@urql/core": "^5.0.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", @@ -3363,13 +3374,13 @@ "wonka": "^6.3.4" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18 || ^19 || ^19.0.0-rc" } }, "node_modules/@copilotkit/shared": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@copilotkit/shared/-/shared-1.3.2.tgz", - "integrity": "sha512-AilGdd6SFN5KdeaT8HUtNrOldAJlnnaQIAE4nmgxl/zOemvNexQptBSa6KwQXsbIViDNMF43IXxSm6x6sew+KQ==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@copilotkit/shared/-/shared-1.3.7.tgz", + "integrity": "sha512-8ZPvEEUdx36lkaJ16aDiYbzXun7WRwsWdVfwJrP14eqamBoTFcg9KaWGbiwqJf4d2ahE4ctx/0gMDGSp4Lwajw==", "dependencies": { "@segment/analytics-node": "^2.1.2", "chalk": "4.1.2", @@ -3844,20 +3855,59 @@ "react": ">=16.3" } }, - "node_modules/@google/generative-ai": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.11.5.tgz", - "integrity": "sha512-DviMgrnljEKh6qkDT2pVFW+NEuVhggqBUoEnyy2PNL7l4ewxXRJubk3PctC9yPl1AdRIlhqP7E076QQt+IWuTg==", - "engines": { - "node": ">=18.0.0" + "node_modules/@frigade/js": { + "version": "0.7.22", + "resolved": "https://registry.npmjs.org/@frigade/js/-/js-0.7.22.tgz", + "integrity": "sha512-Ij0KVpsOyAShQ3re4pFUT58JkLFhFFSFc1XPFUyZnrO1nVfriVw00D87W/RX1kYi9hKfrMABRQKwtgKzRYtEvw==", + "dependencies": { + "uuid": "^9.0.0" + } + }, + "node_modules/@frigade/js/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@frigade/react": { + "version": "2.5.26", + "resolved": "https://registry.npmjs.org/@frigade/react/-/react-2.5.26.tgz", + "integrity": "sha512-/2ZsxwQAdNmKFuBfWYv2gLqJSldqVxFlOo5p7mZbAuhMoqh+J+Y0+Y1VHwh51zC7S8gK+4MKh+tQJ9SBhU9bKw==", + "dependencies": { + "@emotion/react": "^11.11.4", + "@floating-ui/react": "^0.26.22", + "@frigade/js": "^0.7.22", + "@radix-ui/react-checkbox": "^1.1.1", + "@radix-ui/react-collapsible": "^1.0.3", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-radio-group": "^1.1.3", + "@radix-ui/react-select": "^2.0.0", + "clsx": "^2.0.0", + "dompurify": "^3.1.3", + "embla-carousel-react": "^8.1.3", + "jsdom": "^23.0.1", + "known-css-properties": "^0.29.0", + "react-hook-form": "^7.49.3", + "react-remove-scroll": "^2.5.10" + }, + "peerDependencies": { + "react": "17 - 18", + "react-dom": "17 - 18" } }, "node_modules/@graphql-tools/executor": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.3.1.tgz", - "integrity": "sha512-tgJDdGf9SCAm64ofEMZdv925u6/J+eTmv36TGNLxgP2DpCJsZ6gnJ4A+0D28EazDXqJIvMiPd+3d+o3cCRCAnQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.3.2.tgz", + "integrity": "sha512-U8nAR709IPNjwf0aLG6U9FlX0t7vA4cdWvL4RtMR/L/Ll4OHZ39OqUtq6moy+kLRRwLTqLif6iiUYrxnWpUGXw==", "dependencies": { - "@graphql-tools/utils": "^10.3.4", + "@graphql-tools/utils": "^10.5.5", "@graphql-typed-document-node/core": "3.2.0", "@repeaterjs/repeater": "^3.0.4", "tslib": "^2.4.0", @@ -3871,11 +3921,11 @@ } }, "node_modules/@graphql-tools/merge": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.7.tgz", - "integrity": "sha512-lbTrIuXIbUSmSumHkPRY1QX0Z8JEtmRhnIrkH7vkfeEmf0kNn/nCWvJwqokm5U7L+a+DA1wlRM4slIlbfXjJBA==", + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.8.tgz", + "integrity": "sha512-RG9NEp4fi0MoFi0te4ahqTMYuavQnXlpEZxxMomdCa6CI5tfekcVm/rsLF5Zt8O4HY+esDt9+4dCL+aOKvG79w==", "dependencies": { - "@graphql-tools/utils": "^10.5.4", + "@graphql-tools/utils": "^10.5.5", "tslib": "^2.4.0" }, "engines": { @@ -3886,12 +3936,12 @@ } }, "node_modules/@graphql-tools/schema": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.6.tgz", - "integrity": "sha512-EIJgPRGzpvDFEjVp+RF1zNNYIC36BYuIeZ514jFoJnI6IdxyVyIRDLx/ykgMdaa1pKQerpfdqDnsF4JnZoDHSQ==", + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.7.tgz", + "integrity": "sha512-Cz1o+rf9cd3uMgG+zI9HlM5mPlnHQUlk/UQRZyUlPDfT+944taLaokjvj7AI6GcOFVf4f2D11XthQp+0GY31jQ==", "dependencies": { - "@graphql-tools/merge": "^9.0.6", - "@graphql-tools/utils": "^10.5.4", + "@graphql-tools/merge": "^9.0.8", + "@graphql-tools/utils": "^10.5.5", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" }, @@ -3903,9 +3953,9 @@ } }, "node_modules/@graphql-tools/utils": { - "version": "10.5.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.4.tgz", - "integrity": "sha512-XHnyCWSlg1ccsD8s0y6ugo5GZ5TpkTiFVNPSYms5G0s6Z/xTuSmiLBfeqgkfaCwLmLaQnRCmNDL2JRnqc2R5bQ==", + "version": "10.5.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.5.tgz", + "integrity": "sha512-LF/UDWmMT0mnobL2UZETwYghV7HYBzNaGj0SAkCYOMy/C3+6sQdbcTksnoFaKR9XIVD78jNXEGfivbB8Zd+cwA==", "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "cross-inspect": "1.0.1", @@ -4547,6 +4597,39 @@ } } }, + "node_modules/@langchain/community/node_modules/@langchain/core": { + "version": "0.1.63", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.1.63.tgz", + "integrity": "sha512-+fjyYi8wy6x1P+Ee1RWfIIEyxd9Ee9jksEwvrggPwwI/p45kIDTdYTblXsM13y4mNWTiACyLSdbwnPaxxdoz+w==", + "dependencies": { + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": "~0.1.7", + "ml-distance": "^4.0.0", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^9.0.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/community/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/@langchain/community/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -4560,20 +4643,19 @@ } }, "node_modules/@langchain/core": { - "version": "0.1.63", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.1.63.tgz", - "integrity": "sha512-+fjyYi8wy6x1P+Ee1RWfIIEyxd9Ee9jksEwvrggPwwI/p45kIDTdYTblXsM13y4mNWTiACyLSdbwnPaxxdoz+w==", + "version": "0.3.16", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.16.tgz", + "integrity": "sha512-g83M2Z1XlhECFUtT4C7XLsVVGt2Hk3Y/KhS5tZSsz+Gqtxwd790/MD7MxdUHpZj0VKkvrFuWARWpJmNKlkiY+g==", "dependencies": { "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", - "langsmith": "~0.1.7", - "ml-distance": "^4.0.0", + "langsmith": "^0.2.0", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", - "uuid": "^9.0.0", + "uuid": "^10.0.0", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.3" }, @@ -4581,6 +4663,11 @@ "node": ">=18" } }, + "node_modules/@langchain/core/node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==" + }, "node_modules/@langchain/core/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -4592,10 +4679,66 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/@langchain/core/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@langchain/core/node_modules/langsmith": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.2.3.tgz", + "integrity": "sha512-SPMYPVqR9kwXZVmJ2PXC61HeBnXIFHrjfjDxQ14H0+n5p4gqjLzgSHIQyxBlFeWQUQzArJxe65Ap+s+Xo1cZog==", + "dependencies": { + "@types/uuid": "^10.0.0", + "commander": "^10.0.1", + "p-queue": "^6.6.2", + "p-retry": "4", + "semver": "^7.6.3", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "openai": "*" + }, + "peerDependenciesMeta": { + "openai": { + "optional": true + } + } + }, "node_modules/@langchain/core/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/google-common": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@langchain/google-common/-/google-common-0.1.1.tgz", + "integrity": "sha512-oT/6lBev/Ufkp1dJbOTJ2S7xD9c+w9CqnqKqFOSxuZJbM4G8hzJtt7PDBOGfamIwtQP8dR7ORKXs1sCl+f5Tig==", + "dependencies": { + "uuid": "^10.0.0", + "zod-to-json-schema": "^3.22.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.21 <0.4.0" + } + }, + "node_modules/@langchain/google-common/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -4604,6 +4747,21 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@langchain/google-gauth": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@langchain/google-gauth/-/google-gauth-0.1.0.tgz", + "integrity": "sha512-0kps1NmaNiSl4n3lRw+7xsyhrEfIxNqBjih0kNYWPjLg55f9I9+QAlz7F1Sz/628HF1WQLFLQcBQA4geGzvenQ==", + "dependencies": { + "@langchain/google-common": "~0.1.0", + "google-auth-library": "^8.9.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.21 <0.4.0" + } + }, "node_modules/@langchain/openai": { "version": "0.0.28", "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.0.28.tgz", @@ -4619,32 +4777,21 @@ "node": ">=18" } }, - "node_modules/@langchain/textsplitters": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-0.0.3.tgz", - "integrity": "sha512-cXWgKE3sdWLSqAa8ykbCcUsUF1Kyr5J3HOWYGuobhPEycXW4WI++d5DhzdpL238mzoEXTi90VqfSCra37l5YqA==", - "dependencies": { - "@langchain/core": ">0.2.0 <0.3.0", - "js-tiktoken": "^1.0.12" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@langchain/textsplitters/node_modules/@langchain/core": { - "version": "0.2.34", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.2.34.tgz", - "integrity": "sha512-Hkveq1UcOjUj1DVn5erbqElyRj1t04NORSuSIZAJCtPO7EDkIqomjAarJ5+I5NUpQeIONgbOdnY9TkJ6cKUSVA==", + "node_modules/@langchain/openai/node_modules/@langchain/core": { + "version": "0.1.63", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.1.63.tgz", + "integrity": "sha512-+fjyYi8wy6x1P+Ee1RWfIIEyxd9Ee9jksEwvrggPwwI/p45kIDTdYTblXsM13y4mNWTiACyLSdbwnPaxxdoz+w==", "dependencies": { "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", - "langsmith": "^0.1.56-rc.1", + "langsmith": "~0.1.7", + "ml-distance": "^4.0.0", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", - "uuid": "^10.0.0", + "uuid": "^9.0.0", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.3" }, @@ -4652,7 +4799,7 @@ "node": ">=18" } }, - "node_modules/@langchain/textsplitters/node_modules/ansi-styles": { + "node_modules/@langchain/openai/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", @@ -4663,10 +4810,10 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@langchain/textsplitters/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "node_modules/@langchain/openai/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -4675,6 +4822,20 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@langchain/textsplitters": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-0.1.0.tgz", + "integrity": "sha512-djI4uw9rlkAb5iMhtLED+xJebDdAG935AdP4eRTB02R7OB/act55Bj9wsskhZsvuyQRpO4O1wQOp85s6T6GWmw==", + "dependencies": { + "js-tiktoken": "^1.0.12" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.21 <0.4.0" + } + }, "node_modules/@lukeed/csprng": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", @@ -5390,58 +5551,51 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" }, - "node_modules/@radix-ui/react-compose-refs": { + "node_modules/@radix-ui/react-arrow": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", - "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", + "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-dialog": { + "node_modules/@radix-ui/react-checkbox": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz", - "integrity": "sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.2.tgz", + "integrity": "sha512-/i0fl686zaJbDQLNKrkCbMyDm6FQMt4jg323k7HuqitoANm9sE23Ql8yOK3Wusk34HSLKDChhMux05FnP6KUkw==", "dependencies": { "@radix-ui/primitive": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-portal": "1.1.2", "@radix-ui/react-presence": "1.1.1", "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -5458,16 +5612,19 @@ } } }, - "node_modules/@radix-ui/react-dismissable-layer": { + "node_modules/@radix-ui/react-collapsible": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", - "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz", + "integrity": "sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==", "dependencies": { "@radix-ui/primitive": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.1", "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -5484,28 +5641,15 @@ } } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", - "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { + "node_modules/@radix-ui/react-collection": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", - "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", + "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", "dependencies": { "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/react-slot": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -5522,15 +5666,170 @@ } } }, - "node_modules/@radix-ui/react-icons": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", - "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", - "peerDependencies": { - "react": "^16.x || ^17.x || ^18.x" - } - }, - "node_modules/@radix-ui/react-id": { + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz", + "integrity": "sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.6.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", + "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", + "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", + "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x" + } + }, + "node_modules/@radix-ui/react-id": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", @@ -5569,6 +5868,87 @@ } } }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.2.tgz", + "integrity": "sha512-u2HRUyWW+lOiA2g0Le0tMmT55FGOEWHwPFt1EPfbLly7uXQExFo5duNKqG2DzmFXIdqOeNd+TpE8baHWJCyP9w==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.6.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", + "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-portal": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", @@ -5637,6 +6017,123 @@ } } }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.2.1.tgz", + "integrity": "sha512-kdbv54g4vfRjja9DNWPMxKvXblzqbpEC8kspEkZ6dVP7kQksGCn+iZHkcCz2nb00+lPdRvxrqy4WrvvV1cNqrQ==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-roving-focus": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", + "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.2.tgz", + "integrity": "sha512-rZJtWmorC7dFRi0owDmoijm6nSJH1tVw64QGiNIZ9PNLyBDtG+iAq+XGsya052At4BfarzY/Dhv9wrrUr6IMZA==", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.6.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.0.tgz", @@ -5738,6 +6235,81 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "dependencies": { + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz", + "integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==" + }, "node_modules/@react-aria/focus": { "version": "3.18.2", "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.18.2.tgz", @@ -5896,9 +6468,9 @@ } }, "node_modules/@segment/analytics-node/node_modules/jose": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.3.tgz", - "integrity": "sha512-egLIoYSpcd+QUF+UHgobt5YzI2Pkw/H39ou9suW687MY6PmCwPmkNV/4TNjn1p2tX5xO3j0d0sq5hiYE24bSlg==", + "version": "5.9.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz", + "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -6858,9 +7430,9 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, "node_modules/@urql/core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.0.6.tgz", - "integrity": "sha512-38rgSDqVNihFDauw1Pm9V7XLWIKuK8V9CKgrUF7/xEKinze8ENKP1ZeBhkG+dxWzJan7CHK+SLl46kAdvZwIlA==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.0.8.tgz", + "integrity": "sha512-1GOnUw7/a9bzkcM0+U8U5MmxW2A7FE5YquuEmcJzTtW5tIs2EoS4F2ITpuKBjRBbyRjZgO860nWFPo1m4JImGA==", "dependencies": { "@0no-co/graphql.web": "^1.0.5", "wonka": "^6.3.2" @@ -6878,11 +7450,11 @@ } }, "node_modules/@whatwg-node/fetch": { - "version": "0.9.21", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.21.tgz", - "integrity": "sha512-Wt0jPb+04JjobK0pAAN7mEHxVHcGA9HoP3OyCsZtyAecNQeADXCZ1MihFwVwjsgaRYuGVmNlsCmLxlG6mor8Gw==", + "version": "0.9.22", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.22.tgz", + "integrity": "sha512-+RIBffgoaRlWV9cKV6wAX71sbeoU2APOI3G13ZRMkabYHwkvDMeZDTyxJcsMXA5CpieJ7NFXF9Xyu72jwvdzqA==", "dependencies": { - "@whatwg-node/node-fetch": "^0.5.23", + "@whatwg-node/node-fetch": "^0.5.27", "urlpattern-polyfill": "^10.0.0" }, "engines": { @@ -6890,9 +7462,9 @@ } }, "node_modules/@whatwg-node/node-fetch": { - "version": "0.5.26", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.26.tgz", - "integrity": "sha512-4jXDeZ4IH4bylZ6wu14VEx0aDXXhrN4TC279v9rPmn08g4EYekcYf8wdcOOnS9STjDkb6x77/6xBUTqxGgjr8g==", + "version": "0.5.27", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.27.tgz", + "integrity": "sha512-0OaMj5W4fzWimRSFq07qFiWfquaUMNB+695GwE76LYKVuah+jwCdzSgsIOtwPkiyJ35w0XGhXmJPiIJCdLwopg==", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "busboy": "^1.6.0", @@ -6904,11 +7476,11 @@ } }, "node_modules/@whatwg-node/server": { - "version": "0.9.49", - "resolved": "https://registry.npmjs.org/@whatwg-node/server/-/server-0.9.49.tgz", - "integrity": "sha512-3KzLXw80gWnTsQ746G/LFdCThTPfDodjQs4PnmoNuPa6XUOl4HWq8TlJpxtmnEEB+y+UYLal+3VQ68dtYlbUDQ==", + "version": "0.9.50", + "resolved": "https://registry.npmjs.org/@whatwg-node/server/-/server-0.9.50.tgz", + "integrity": "sha512-7Vd8k6iu+ps8bkZT+Y/wPm42EDh8KojAL+APKa79mntgkyPtdq0r1//CO+0eYqQBz6HGrDxHRT4KChSOy4jGIw==", "dependencies": { - "@whatwg-node/fetch": "^0.9.21", + "@whatwg-node/fetch": "^0.9.22", "tslib": "^2.6.3" }, "engines": { @@ -7007,6 +7579,17 @@ "resolved": "https://registry.npmjs.org/add/-/add-2.0.6.tgz", "integrity": "sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==" }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/agentkeepalive": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", @@ -7292,6 +7875,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "engines": { + "node": ">=8" + } + }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -7496,11 +8087,6 @@ "bare-os": "^2.1.0" } }, - "node_modules/base-64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", - "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" - }, "node_modules/base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", @@ -7528,6 +8114,14 @@ } ] }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -7536,6 +8130,14 @@ "node": ">=0.6" } }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -7703,6 +8305,11 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -7930,14 +8537,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "engines": { - "node": "*" - } - }, "node_modules/chart.js": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", @@ -8532,9 +9131,9 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.0.tgz", - "integrity": "sha512-qCf+V4dtlNhSRXGAZatc1TasyFO6GjohcOul807YOb5ik3+kQSnb4d7iajeCL8QHaJ4uZEjCgiCJerKXwdRVlQ==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -8544,6 +9143,16 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-js": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.36.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", @@ -8613,14 +9222,6 @@ "node": ">= 8" } }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "engines": { - "node": "*" - } - }, "node_modules/css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -8779,6 +9380,22 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" }, + "node_modules/cssstyle": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", + "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "dependencies": { + "rrweb-cssom": "^0.7.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -9041,6 +9658,49 @@ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -9147,6 +9807,11 @@ "node": ">=0.10.0" } }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, "node_modules/decimal.js-light": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", @@ -9389,15 +10054,6 @@ "node": ">=0.3.1" } }, - "node_modules/digest-fetch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", - "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", - "dependencies": { - "base-64": "^0.1.0", - "md5": "^2.3.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -9484,6 +10140,11 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz", + "integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==" + }, "node_modules/domutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", @@ -9595,6 +10256,14 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -9624,6 +10293,31 @@ "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==", "dev": true }, + "node_modules/embla-carousel": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.3.1.tgz", + "integrity": "sha512-DutFjtEO586XptDn4cwvBJwsR/8fMa4jUk5Jk2g+/elKgu8mdn0Z2sx33g4JskvbLc1/6P8Xg4QlfELGJFcP5A==" + }, + "node_modules/embla-carousel-react": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.3.1.tgz", + "integrity": "sha512-gBY0zM+2ASvKFwRpTIOn2SLifFqOKKap9R/y0iCpJWS3bc8OHVEn2gAThGYl2uq0N+hu9aBiswffL++OYZOmDQ==", + "dependencies": { + "embla-carousel": "8.3.1", + "embla-carousel-reactive-utils": "8.3.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/embla-carousel-reactive-utils": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.3.1.tgz", + "integrity": "sha512-Js6rTTINNGnUGPu7l5kTcheoSbEnP5Ak2iX0G9uOoI8okTNLMzuWlEIpYFd1WP0Sq82FFcLkKM2oiO6jcElZ/Q==", + "peerDependencies": { + "embla-carousel": "8.3.1" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -10546,16 +11240,16 @@ "integrity": "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==" }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -10586,14 +11280,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -10741,6 +11427,11 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "node_modules/fast-text-encoding": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", + "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -11109,6 +11800,66 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gaxios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gaxios/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gcp-metadata": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "dependencies": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -11553,6 +12304,40 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/google-auth-library": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz", + "integrity": "sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==", + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^5.0.0", + "gcp-metadata": "^5.3.0", + "gtoken": "^6.1.0", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/google-p12-pem": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", + "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", + "deprecated": "Package is no longer maintained", + "dependencies": { + "node-forge": "^1.3.1" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -11657,9 +12442,9 @@ } }, "node_modules/groq-sdk/node_modules/@types/node": { - "version": "18.19.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz", - "integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==", + "version": "18.19.62", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.62.tgz", + "integrity": "sha512-UOGhw+yZV/icyM0qohQVh3ktpY40Sp7tdTW7HxG3pTd7AiMrlFlAJNUrGK9t5mdW0+ViQcFV74zCSIx9ZJpncA==", "dependencies": { "undici-types": "~5.26.4" } @@ -11672,6 +12457,19 @@ "node": ">= 8" } }, + "node_modules/gtoken": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", + "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "dependencies": { + "gaxios": "^5.0.1", + "google-p12-pem": "^4.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -12032,6 +12830,17 @@ "resolved": "https://registry.npmjs.org/hsluv/-/hsluv-0.0.3.tgz", "integrity": "sha512-08iL2VyCRbkQKBySkSh6m8zMUa3sADAxGVWs3Z1aPcUkTJeK0ETG4Fc27tEmQBGUAXZjIsXOZqBvacuVNSC/fQ==" }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -12072,6 +12881,30 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/human-signals": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", @@ -12651,6 +13484,11 @@ "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -12920,6 +13758,76 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.2.0.tgz", + "integrity": "sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==", + "dependencies": { + "@asamuzakjp/dom-selector": "^2.0.1", + "cssstyle": "^4.0.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.16.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -12931,6 +13839,14 @@ "node": ">=4" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -12984,6 +13900,25 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/katex": { "version": "0.16.11", "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.11.tgz", @@ -13028,6 +13963,11 @@ "node": ">=6" } }, + "node_modules/known-css-properties": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.29.0.tgz", + "integrity": "sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==" + }, "node_modules/kysely": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.3.tgz", @@ -13050,276 +13990,139 @@ } }, "node_modules/langchain": { - "version": "0.1.37", - "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.1.37.tgz", - "integrity": "sha512-rpaLEJtRrLYhAViEp7/aHfSkxbgSqHJ5n10tXv3o4kHP/wOin85RpTgewwvGjEaKc3797jOg+sLSk6a7e0UlMg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.3.5.tgz", + "integrity": "sha512-Gq0xC45Sq6nszS8kQG9suCrmBsuXH0INMmiF7D2TwPb6mtG35Jiq4grCk9ykpwPsarTHdty3SzUbII/FqiYSSw==", "dependencies": { - "@anthropic-ai/sdk": "^0.9.1", - "@langchain/community": "~0.0.47", - "@langchain/core": "~0.1.60", - "@langchain/openai": "~0.0.28", - "@langchain/textsplitters": "~0.0.0", - "binary-extensions": "^2.2.0", - "js-tiktoken": "^1.0.7", + "@langchain/openai": ">=0.1.0 <0.4.0", + "@langchain/textsplitters": ">=0.0.0 <0.2.0", + "js-tiktoken": "^1.0.12", "js-yaml": "^4.1.0", "jsonpointer": "^5.0.1", - "langchainhub": "~0.0.8", - "langsmith": "~0.1.7", - "ml-distance": "^4.0.0", - "openapi-types": "^12.1.3", - "p-retry": "4", - "uuid": "^9.0.0", - "yaml": "^2.2.1", - "zod": "^3.22.4", - "zod-to-json-schema": "^3.22.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@aws-sdk/client-s3": "^3.310.0", - "@aws-sdk/client-sagemaker-runtime": "^3.310.0", - "@aws-sdk/client-sfn": "^3.310.0", - "@aws-sdk/credential-provider-node": "^3.388.0", - "@azure/storage-blob": "^12.15.0", - "@browserbasehq/sdk": "*", - "@gomomento/sdk": "^1.51.1", - "@gomomento/sdk-core": "^1.51.1", - "@gomomento/sdk-web": "^1.51.1", - "@google-ai/generativelanguage": "^0.2.1", - "@google-cloud/storage": "^6.10.1 || ^7.7.0", - "@mendable/firecrawl-js": "^0.0.13", - "@notionhq/client": "^2.2.10", - "@pinecone-database/pinecone": "*", - "@supabase/supabase-js": "^2.10.0", - "@vercel/kv": "^0.2.3", - "@xata.io/client": "^0.28.0", - "apify-client": "^2.7.1", - "assemblyai": "^4.0.0", - "axios": "*", - "cheerio": "^1.0.0-rc.12", - "chromadb": "*", - "convex": "^1.3.1", - "couchbase": "^4.3.0", - "d3-dsv": "^2.0.0", - "epub2": "^3.0.1", - "fast-xml-parser": "*", - "google-auth-library": "^8.9.0", - "handlebars": "^4.7.8", - "html-to-text": "^9.0.5", - "ignore": "^5.2.0", - "ioredis": "^5.3.2", - "jsdom": "*", - "mammoth": "^1.6.0", - "mongodb": ">=5.2.0", - "node-llama-cpp": "*", - "notion-to-md": "^3.1.0", - "officeparser": "^4.0.4", - "pdf-parse": "1.1.1", - "peggy": "^3.0.2", - "playwright": "^1.32.1", - "puppeteer": "^19.7.2", - "pyodide": "^0.24.1", - "redis": "^4.6.4", - "sonix-speech-recognition": "^2.1.1", - "srt-parser-2": "^1.2.3", - "typeorm": "^0.3.12", - "weaviate-ts-client": "*", - "web-auth-library": "^1.0.3", - "ws": "^8.14.2", - "youtube-transcript": "^1.0.6", - "youtubei.js": "^9.1.0" - }, - "peerDependenciesMeta": { - "@aws-sdk/client-s3": { - "optional": true - }, - "@aws-sdk/client-sagemaker-runtime": { - "optional": true - }, - "@aws-sdk/client-sfn": { - "optional": true - }, - "@aws-sdk/credential-provider-node": { - "optional": true - }, - "@azure/storage-blob": { - "optional": true - }, - "@browserbasehq/sdk": { - "optional": true - }, - "@gomomento/sdk": { - "optional": true - }, - "@gomomento/sdk-core": { - "optional": true - }, - "@gomomento/sdk-web": { - "optional": true - }, - "@google-ai/generativelanguage": { - "optional": true - }, - "@google-cloud/storage": { - "optional": true - }, - "@mendable/firecrawl-js": { - "optional": true - }, - "@notionhq/client": { - "optional": true - }, - "@pinecone-database/pinecone": { - "optional": true - }, - "@supabase/supabase-js": { - "optional": true - }, - "@vercel/kv": { - "optional": true - }, - "@xata.io/client": { - "optional": true - }, - "apify-client": { - "optional": true - }, - "assemblyai": { - "optional": true - }, - "axios": { + "langsmith": "^0.2.0", + "openapi-types": "^12.1.3", + "p-retry": "4", + "uuid": "^10.0.0", + "yaml": "^2.2.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/anthropic": "*", + "@langchain/aws": "*", + "@langchain/cohere": "*", + "@langchain/core": ">=0.2.21 <0.4.0", + "@langchain/google-genai": "*", + "@langchain/google-vertexai": "*", + "@langchain/groq": "*", + "@langchain/mistralai": "*", + "@langchain/ollama": "*", + "axios": "*", + "cheerio": "*", + "handlebars": "^4.7.8", + "peggy": "^3.0.2", + "typeorm": "*" + }, + "peerDependenciesMeta": { + "@langchain/anthropic": { "optional": true }, - "cheerio": { + "@langchain/aws": { "optional": true }, - "chromadb": { + "@langchain/cohere": { "optional": true }, - "convex": { + "@langchain/google-genai": { "optional": true }, - "couchbase": { + "@langchain/google-vertexai": { "optional": true }, - "d3-dsv": { + "@langchain/groq": { "optional": true }, - "epub2": { + "@langchain/mistralai": { "optional": true }, - "faiss-node": { + "@langchain/ollama": { "optional": true }, - "fast-xml-parser": { + "axios": { "optional": true }, - "google-auth-library": { + "cheerio": { "optional": true }, "handlebars": { "optional": true }, - "html-to-text": { - "optional": true - }, - "ignore": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "mammoth": { - "optional": true - }, - "mongodb": { - "optional": true - }, - "node-llama-cpp": { - "optional": true - }, - "notion-to-md": { - "optional": true - }, - "officeparser": { - "optional": true - }, - "pdf-parse": { - "optional": true - }, "peggy": { "optional": true }, - "playwright": { - "optional": true - }, - "puppeteer": { - "optional": true - }, - "pyodide": { - "optional": true - }, - "redis": { - "optional": true - }, - "sonix-speech-recognition": { - "optional": true - }, - "srt-parser-2": { - "optional": true - }, "typeorm": { "optional": true - }, - "weaviate-ts-client": { - "optional": true - }, - "web-auth-library": { - "optional": true - }, - "ws": { - "optional": true - }, - "youtube-transcript": { - "optional": true - }, - "youtubei.js": { - "optional": true } } }, - "node_modules/langchain/node_modules/@anthropic-ai/sdk": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.9.1.tgz", - "integrity": "sha512-wa1meQ2WSfoY8Uor3EdrJq0jTiZJoKoSii2ZVWRY1oN4Tlr5s59pADg9T79FTbPe1/se5c3pBeZgJL63wmuoBA==", + "node_modules/langchain/node_modules/@langchain/openai": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.3.11.tgz", + "integrity": "sha512-mEFbpJ8w8NPArsquUlCwxvZTKNkXxqwzvTEYzv6Jb7gUoBDOZtwLg6AdcngTJ+w5VFh3wxgPy0g3zb9Aw0Qbpw==", "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "digest-fetch": "^1.3.0", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7", - "web-streams-polyfill": "^3.2.1" + "js-tiktoken": "^1.0.12", + "openai": "^4.68.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.26 <0.4.0" + } + }, + "node_modules/langchain/node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==" + }, + "node_modules/langchain/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" } }, - "node_modules/langchain/node_modules/@types/node": { - "version": "18.19.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz", - "integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==", + "node_modules/langchain/node_modules/langsmith": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.2.3.tgz", + "integrity": "sha512-SPMYPVqR9kwXZVmJ2PXC61HeBnXIFHrjfjDxQ14H0+n5p4gqjLzgSHIQyxBlFeWQUQzArJxe65Ap+s+Xo1cZog==", "dependencies": { - "undici-types": "~5.26.4" + "@types/uuid": "^10.0.0", + "commander": "^10.0.1", + "p-queue": "^6.6.2", + "p-retry": "4", + "semver": "^7.6.3", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "openai": "*" + }, + "peerDependenciesMeta": { + "openai": { + "optional": true + } } }, "node_modules/langchain/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -13328,23 +14131,10 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/langchain/node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/langchainhub": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/langchainhub/-/langchainhub-0.0.11.tgz", - "integrity": "sha512-WnKI4g9kU2bHQP136orXr2bcRdgz9iiTBpTN0jWt9IlScUKnJBoD0aa2HOzHURQKeQDnt2JwqVmQ6Depf5uDLQ==" - }, "node_modules/langsmith": { - "version": "0.1.61", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.1.61.tgz", - "integrity": "sha512-XQE4KPScwPmdaT0mWDzhNxj9gvqXUR+C7urLA0QFi27XeoQdm17eYpudenn4wxC0gIyUJutQCyuYJpfwlT5JnQ==", + "version": "0.1.68", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.1.68.tgz", + "integrity": "sha512-otmiysWtVAqzMx3CJ4PrtUBhWRG5Co8Z4o7hSZENPjlit9/j3/vm3TSvbaxpDYakZxtMjhkcJTqrdYFipISEiQ==", "dependencies": { "@types/uuid": "^10.0.0", "commander": "^10.0.1", @@ -13416,9 +14206,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.11.11", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.11.tgz", - "integrity": "sha512-mF3KaORjJQR6JBNcOkluDcJKhtoQT4VTLRMrX1v/wlBayL4M8ybwEDeryyPcrSEJmD0rVwHUbBarpZwN5NfPFQ==" + "version": "1.11.12", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.12.tgz", + "integrity": "sha512-QkJn9/D7zZ1ucvT++TQSvZuSA2xAWeUytU+DiEQwbPKLyrDpvbul2AFs1CGbRAPpSCCk47aRAb5DX5mmcayp4g==" }, "node_modules/lilconfig": { "version": "2.1.0", @@ -13702,21 +14492,6 @@ "node": ">=0.10.0" } }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "node_modules/md5/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, "node_modules/mdast-util-definitions": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", @@ -15383,6 +16158,14 @@ } } }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -15661,9 +16444,9 @@ } }, "node_modules/openai": { - "version": "4.67.1", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.67.1.tgz", - "integrity": "sha512-2YbRFy6qaYRJabK2zLMn4txrB2xBy0KP5g/eoqeSPTT31mIJMnkT75toagvfE555IKa2RdrzJrZwdDsUipsAMw==", + "version": "4.69.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.69.0.tgz", + "integrity": "sha512-S3hOHSkk609KqwgH+7dwFrSvO3Gm3Nk0YWGyPHNscoMH/Y2tH1qunMi7gtZnLbUv4/N1elqCp6bDior2401kCQ==", "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", @@ -15686,9 +16469,9 @@ } }, "node_modules/openai/node_modules/@types/node": { - "version": "18.19.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz", - "integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==", + "version": "18.19.62", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.62.tgz", + "integrity": "sha512-UOGhw+yZV/icyM0qohQVh3ktpY40Sp7tdTW7HxG3pTd7AiMrlFlAJNUrGK9t5mdW0+ViQcFV74zCSIx9ZJpncA==", "dependencies": { "undici-types": "~5.26.4" } @@ -16028,14 +16811,14 @@ } }, "node_modules/pino": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-9.4.0.tgz", - "integrity": "sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.5.0.tgz", + "integrity": "sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw==", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.2.0", + "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^4.0.0", "quick-format-unescaped": "^4.0.3", @@ -16049,56 +16832,17 @@ } }, "node_modules/pino-abstract-transport": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", - "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", "dependencies": { - "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, - "node_modules/pino-abstract-transport/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/pino-pretty": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.2.2.tgz", - "integrity": "sha512-2FnyGir8nAJAqD3srROdrF1J5BIcMT4nwj7hHSc60El6Uxlym00UbCCd8pYIterstVBFlMyF1yFV8XdGIPbj4A==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.3.0.tgz", + "integrity": "sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==", "dependencies": { "colorette": "^2.0.7", "dateformat": "^4.6.3", @@ -16108,7 +16852,7 @@ "joycon": "^3.1.1", "minimist": "^1.2.6", "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.0.0", + "pino-abstract-transport": "^2.0.0", "pump": "^3.0.0", "readable-stream": "^4.0.0", "secure-json-parse": "^2.4.0", @@ -16404,13 +17148,14 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/posthog-js": { - "version": "1.168.0", - "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.168.0.tgz", - "integrity": "sha512-FxYxdXe407QsK8bqWnTVLcjhJnvOY3X3++4Lc1qOHPChln/e8w04uoLqLUIEfpFmVJBLM6XrsdvTWYETukOcPg==", + "version": "1.178.0", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.178.0.tgz", + "integrity": "sha512-ILD4flNh72d5dycc4ZouKORlaVr+pDzl5TlZr1JgP0NBAoduHjhE7XZYwk7zdYkry7u0qAIpfv2306zJCW2vGg==", "dependencies": { + "core-js": "^3.38.1", "fflate": "^0.4.8", "preact": "^10.19.3", - "web-vitals": "^4.0.1" + "web-vitals": "^4.2.0" } }, "node_modules/posthog-node": { @@ -16800,6 +17545,11 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -16839,6 +17589,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -18106,12 +18861,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-relative": { "version": "0.8.7", "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", "dev": true }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", @@ -18193,6 +18961,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==" + }, "node_modules/run-applescript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", @@ -18426,6 +19199,17 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -18778,9 +19562,9 @@ } }, "node_modules/sonic-boom": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.1.0.tgz", - "integrity": "sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", "dependencies": { "atomic-sleep": "^1.0.0" } @@ -19428,6 +20212,11 @@ "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, "node_modules/tabbable": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", @@ -19688,6 +20477,20 @@ "node": ">=6" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -20107,6 +20910,14 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -20175,15 +20986,24 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/urlpattern-polyfill": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==" }, "node_modules/urql": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/urql/-/urql-4.1.0.tgz", - "integrity": "sha512-NfbfTvxy1sM89EQAJWm89qJZihUWk7BSMfrWgfljFXLOf+e7RK7DtV/Tbg2+82HnCG2x3LcEOJenxiFSYEC+bw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/urql/-/urql-4.2.0.tgz", + "integrity": "sha512-xj9KoMxdF3omd0QCHHS3SvgxDHZAmuAFgUcdNe0w75NeJDA4qesc2/M6CquefZ7Sod6uyImENKCKg+/2I7YvXQ==", "dependencies": { "@urql/core": "^5.0.0", "wonka": "^6.3.2" @@ -20424,6 +21244,17 @@ "eslint": ">=6.0.0" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", @@ -20530,6 +21361,36 @@ } } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -20737,8 +21598,6 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "optional": true, - "peer": true, "engines": { "node": ">=10.0.0" }, @@ -20755,6 +21614,19 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, "node_modules/xss": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz", @@ -20819,9 +21691,9 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.23.3", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.3.tgz", - "integrity": "sha512-TYWChTxKQbRJp5ST22o/Irt9KC5nj7CdBKYB/AosCRdj/wxEMvv4NNaj9XVUHDOIp53ZxArGhnw5HMZziPFjog==", + "version": "3.23.5", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.5.tgz", + "integrity": "sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==", "peerDependencies": { "zod": "^3.23.3" } diff --git a/keep-ui/package.json b/keep-ui/package.json index 089b83050..f88fa01fb 100644 --- a/keep-ui/package.json +++ b/keep-ui/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@boiseitguru/cookie-cutter": "^0.2.3", - "@copilotkit/react-core": "^1.3.2", - "@copilotkit/react-textarea": "^1.3.2", - "@copilotkit/react-ui": "^1.3.2", + "@copilotkit/react-core": "^1.3.7", + "@copilotkit/react-textarea": "^1.3.7", + "@copilotkit/react-ui": "^1.3.7", "@dagrejs/dagre": "^1.1.3", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", @@ -25,6 +25,7 @@ "@fortawesome/free-brands-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", + "@frigade/react": "^2.5.26", "@headlessui/react": "^1.7.14", "@heroicons/react": "^2.1.5", "@mui/material": "^5.15.18", @@ -274,7 +275,7 @@ "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.12", "postcss-value-parser": "^4.2.0", - "posthog-js": "^1.168.0", + "posthog-js": "^1.178.0", "posthog-node": "^3.1.1", "preact-render-to-string": "^5.2.6", "prelude-ls": "^1.2.1", diff --git a/keep-ui/utils/hooks/useDashboardMetricWidgets.ts b/keep-ui/utils/hooks/useDashboardMetricWidgets.ts new file mode 100644 index 000000000..d7d24ecc8 --- /dev/null +++ b/keep-ui/utils/hooks/useDashboardMetricWidgets.ts @@ -0,0 +1,66 @@ + import {useSession} from "next-auth/react"; + import { useApiUrl } from "./useConfig"; +import useSWR from "swr"; +import {fetcher} from "@/utils/fetcher"; +import { usePathname, useSearchParams } from "next/navigation"; + +export interface MetricsWidget { + id: string; + name: string; + data: DistributionData[]; +} + +interface DistributionData { + hour: string; + number: number +} + +interface DashboardDistributionData { + mttr: DistributionData[]; + ipd: DistributionData[]; + apd: DistributionData[]; + wpd: DistributionData[]; + +} + +export const useDashboardMetricWidgets = (useFilters?: boolean) => { + const {data: session} = useSession(); + const apiUrl = useApiUrl(); + const searchParams = useSearchParams(); + const filters = searchParams?.toString(); + + const {data, error, mutate} = useSWR( + session ? `${apiUrl}/dashboard/metric-widgets${ + useFilters && filters ? `?${filters}` : "" + }` : null, + (url: string) => fetcher(url, session!.accessToken) + ) + console.log(filters) + + let widgets: MetricsWidget[] = [] + if (data) { + widgets = [ + { + id: "mttr", + name: "MTTR", + data: data.mttr + }, + { + id: "apd", + "name": "Alerts/Day", + data: data.apd + }, + { + id: "ipd", + name: "Incidents/Day", + data: data.ipd + }, + { + id: "wpd", + name: "Workflows/Day", + data: data.wpd + } + ]; + } + return {widgets}; +} \ No newline at end of file diff --git a/keep/api/core/db.py b/keep/api/core/db.py index bd4471b1d..6976faa3a 100644 --- a/keep/api/core/db.py +++ b/keep/api/core/db.py @@ -44,6 +44,7 @@ from keep.api.models.db.tenant import * # pylint: disable=unused-wildcard-import from keep.api.models.db.topology import * # pylint: disable=unused-wildcard-import from keep.api.models.db.workflow import * # pylint: disable=unused-wildcard-import +from keep.api.models.time_stamp import TimeStampFilter logger = logging.getLogger(__name__) @@ -1016,7 +1017,11 @@ def get_enrichment_with_session(session, tenant_id, fingerprint, refresh=False): def get_alerts_with_filters( - tenant_id, provider_id=None, filters=None, time_delta=1, with_incidents=False, + tenant_id, + provider_id=None, + filters=None, + time_delta=1, + with_incidents=False, ) -> list[Alert]: with Session(engine) as session: # Create the query @@ -1190,7 +1195,7 @@ def get_last_alerts( Returns: List[Alert]: A list of Alert objects including the first time the alert was triggered. """ - with (Session(engine) as session): + with Session(engine) as session: # Subquery that selects the max and min timestamp for each fingerprint. subquery = ( session.query( @@ -1254,7 +1259,10 @@ def get_last_alerts( query = query.add_columns(AlertToIncident.incident_id.label("incident")) query = query.outerjoin( AlertToIncident, - and_(AlertToIncident.alert_id == Alert.id, AlertToIncident.deleted_at == NULL_FOR_DELETED_AT), + and_( + AlertToIncident.alert_id == Alert.id, + AlertToIncident.deleted_at == NULL_FOR_DELETED_AT, + ), ) if provider_id: @@ -1718,7 +1726,7 @@ def get_rule_distribution(tenant_id, minute=False): .join(AlertToIncident, Incident.id == AlertToIncident.incident_id) .filter( AlertToIncident.deleted_at == NULL_FOR_DELETED_AT, - AlertToIncident.timestamp >= seven_days_ago + AlertToIncident.timestamp >= seven_days_ago, ) .filter(Rule.tenant_id == tenant_id) # Filter by tenant_id .group_by( @@ -2092,12 +2100,47 @@ def get_linked_providers(tenant_id: str) -> List[Tuple[str, str, datetime]]: return providers -def get_provider_distribution(tenant_id: str) -> dict: - """Returns hits per hour and the last alert timestamp for each provider, limited to the last 24 hours.""" +def get_provider_distribution( + tenant_id: str, + aggregate_all: bool = False, + timestamp_filter: TimeStampFilter = None, +) -> ( + list[dict[str, int | Any]] + | dict[str, dict[str, datetime | list[dict[str, int]] | Any]] +): + """ + Calculate the distribution of incidents created over time for a specific tenant. + + Args: + tenant_id (str): ID of the tenant whose incidents are being queried. + timestamp_filter (TimeStampFilter, optional): Filter to specify the time range. + - lower_timestamp (datetime): Start of the time range. + - upper_timestamp (datetime): End of the time range. + + Returns: + List[dict]: A list of dictionaries representing the hourly distribution of incidents. + Each dictionary contains: + - 'timestamp' (str): Timestamp of the hour in "YYYY-MM-DD HH:00" format. + - 'number' (int): Number of incidents created in that hour. + + Notes: + - If no timestamp_filter is provided, defaults to the last 24 hours. + - Supports MySQL, PostgreSQL, and SQLite for timestamp formatting. + """ with Session(engine) as session: twenty_four_hours_ago = datetime.utcnow() - timedelta(hours=24) time_format = "%Y-%m-%d %H" + filters = [Alert.tenant_id == tenant_id] + + if timestamp_filter: + if timestamp_filter.lower_timestamp: + filters.append(Alert.timestamp >= timestamp_filter.lower_timestamp) + if timestamp_filter.upper_timestamp: + filters.append(Alert.timestamp <= timestamp_filter.upper_timestamp) + else: + filters.append(Alert.timestamp >= twenty_four_hours_ago) + if session.bind.dialect.name == "mysql": timestamp_format = func.date_format(Alert.timestamp, time_format) elif session.bind.dialect.name == "postgresql": @@ -2107,62 +2150,339 @@ def get_provider_distribution(tenant_id: str) -> dict: elif session.bind.dialect.name == "sqlite": timestamp_format = func.strftime(time_format, Alert.timestamp) - # Adjusted query to include max timestamp + if aggregate_all: + # Query for combined alert distribution across all providers + query = ( + session.query( + timestamp_format.label("time"), func.count().label("hits") + ) + .filter(*filters) + .group_by("time") + .order_by("time") + ) + + results = query.all() + + results = {str(time): hits for time, hits in results} + + # Create a complete list of timestamps within the specified range + distribution = [] + current_time = timestamp_filter.lower_timestamp.replace( + minute=0, second=0, microsecond=0 + ) + while current_time <= timestamp_filter.upper_timestamp: + timestamp_str = current_time.strftime(time_format) + distribution.append( + { + "timestamp": timestamp_str + ":00", + "number": results.get(timestamp_str, 0), + } + ) + current_time += timedelta(hours=1) + return distribution + + else: + # Query for alert distribution grouped by provider + query = ( + session.query( + Alert.provider_id, + Alert.provider_type, + timestamp_format.label("time"), + func.count().label("hits"), + func.max(Alert.timestamp).label("last_alert_timestamp"), + ) + .filter(*filters) + .group_by(Alert.provider_id, Alert.provider_type, "time") + .order_by(Alert.provider_id, Alert.provider_type, "time") + ) + + results = query.all() + + provider_distribution = {} + + for provider_id, provider_type, time, hits, last_alert_timestamp in results: + provider_key = f"{provider_id}_{provider_type}" + last_alert_timestamp = ( + datetime.fromisoformat(last_alert_timestamp) + if isinstance(last_alert_timestamp, str) + else last_alert_timestamp + ) + + if provider_key not in provider_distribution: + provider_distribution[provider_key] = { + "provider_id": provider_id, + "provider_type": provider_type, + "alert_last_24_hours": [ + {"hour": i, "number": 0} for i in range(24) + ], + "last_alert_received": last_alert_timestamp, + } + else: + provider_distribution[provider_key]["last_alert_received"] = max( + provider_distribution[provider_key]["last_alert_received"], + last_alert_timestamp, + ) + + time = datetime.strptime(time, time_format) + index = int((time - twenty_four_hours_ago).total_seconds() // 3600) + + if 0 <= index < 24: + provider_distribution[provider_key]["alert_last_24_hours"][index][ + "number" + ] += hits + + return provider_distribution + + +def get_combined_workflow_execution_distribution( + tenant_id: str, timestamp_filter: TimeStampFilter = None +): + """ + Calculate the distribution of WorkflowExecutions started over time, combined across all workflows for a specific tenant. + + Args: + tenant_id (str): ID of the tenant whose workflow executions are being analyzed. + timestamp_filter (TimeStampFilter, optional): Filter to specify the time range. + - lower_timestamp (datetime): Start of the time range. + - upper_timestamp (datetime): End of the time range. + + Returns: + List[dict]: A list of dictionaries representing the hourly distribution of workflow executions. + Each dictionary contains: + - 'timestamp' (str): Timestamp of the hour in "YYYY-MM-DD HH:00" format. + - 'number' (int): Number of workflow executions started in that hour. + + Notes: + - If no timestamp_filter is provided, defaults to the last 24 hours. + - Supports MySQL, PostgreSQL, and SQLite for timestamp formatting. + """ + with Session(engine) as session: + twenty_four_hours_ago = datetime.utcnow() - timedelta(hours=24) + time_format = "%Y-%m-%d %H" + + filters = [WorkflowExecution.tenant_id == tenant_id] + + if timestamp_filter: + if timestamp_filter.lower_timestamp: + filters.append( + WorkflowExecution.started >= timestamp_filter.lower_timestamp + ) + if timestamp_filter.upper_timestamp: + filters.append( + WorkflowExecution.started <= timestamp_filter.upper_timestamp + ) + else: + filters.append(WorkflowExecution.started >= twenty_four_hours_ago) + + # Database-specific timestamp formatting + if session.bind.dialect.name == "mysql": + timestamp_format = func.date_format(WorkflowExecution.started, time_format) + elif session.bind.dialect.name == "postgresql": + timestamp_format = func.to_char(WorkflowExecution.started, "YYYY-MM-DD HH") + elif session.bind.dialect.name == "sqlite": + timestamp_format = func.strftime(time_format, WorkflowExecution.started) + + # Query for combined execution count across all workflows query = ( session.query( - Alert.provider_id, - Alert.provider_type, timestamp_format.label("time"), - func.count().label("hits"), - func.max(Alert.timestamp).label( - "last_alert_timestamp" - ), # Include max timestamp - ) - .filter( - Alert.tenant_id == tenant_id, - Alert.timestamp >= twenty_four_hours_ago, + func.count().label("executions"), ) - .group_by(Alert.provider_id, Alert.provider_type, "time") - .order_by(Alert.provider_id, Alert.provider_type, "time") + .filter(*filters) + .group_by("time") + .order_by("time") ) - results = query.all() + results = {str(time): executions for time, executions in query.all()} + + distribution = [] + current_time = timestamp_filter.lower_timestamp.replace( + minute=0, second=0, microsecond=0 + ) + while current_time <= timestamp_filter.upper_timestamp: + timestamp_str = current_time.strftime(time_format) + distribution.append( + { + "timestamp": timestamp_str + ":00", + "number": results.get(timestamp_str, 0), + } + ) + current_time += timedelta(hours=1) + + return distribution + + +def get_incidents_created_distribution( + tenant_id: str, timestamp_filter: TimeStampFilter = None +): + """ + Calculate the distribution of incidents created over time for a specific tenant. + + Args: + tenant_id (str): ID of the tenant whose incidents are being queried. + timestamp_filter (TimeStampFilter, optional): Filter to specify the time range. + - lower_timestamp (datetime): Start of the time range. + - upper_timestamp (datetime): End of the time range. + + Returns: + List[dict]: A list of dictionaries representing the hourly distribution of incidents. + Each dictionary contains: + - 'timestamp' (str): Timestamp of the hour in "YYYY-MM-DD HH:00" format. + - 'number' (int): Number of incidents created in that hour. + + Notes: + - If no timestamp_filter is provided, defaults to the last 24 hours. + - Supports MySQL, PostgreSQL, and SQLite for timestamp formatting. + """ + with Session(engine) as session: + twenty_four_hours_ago = datetime.utcnow() - timedelta(hours=24) + time_format = "%Y-%m-%d %H" + + filters = [Incident.tenant_id == tenant_id] - provider_distribution = {} + if timestamp_filter: + if timestamp_filter.lower_timestamp: + filters.append( + Incident.creation_time >= timestamp_filter.lower_timestamp + ) + if timestamp_filter.upper_timestamp: + filters.append( + Incident.creation_time <= timestamp_filter.upper_timestamp + ) + else: + filters.append(Incident.creation_time >= twenty_four_hours_ago) + + # Database-specific timestamp formatting + if session.bind.dialect.name == "mysql": + timestamp_format = func.date_format(Incident.creation_time, time_format) + elif session.bind.dialect.name == "postgresql": + timestamp_format = func.to_char(Incident.creation_time, "YYYY-MM-DD HH") + elif session.bind.dialect.name == "sqlite": + timestamp_format = func.strftime(time_format, Incident.creation_time) - for provider_id, provider_type, time, hits, last_alert_timestamp in results: - provider_key = f"{provider_id}_{provider_type}" - last_alert_timestamp = ( - datetime.fromisoformat(last_alert_timestamp) - if isinstance(last_alert_timestamp, str) - else last_alert_timestamp + query = ( + session.query( + timestamp_format.label("time"), func.count().label("incidents") ) + .filter(*filters) + .group_by("time") + .order_by("time") + ) - if provider_key not in provider_distribution: - provider_distribution[provider_key] = { - "provider_id": provider_id, - "provider_type": provider_type, - "alert_last_24_hours": [ - {"hour": i, "number": 0} for i in range(24) - ], - "last_alert_received": last_alert_timestamp, # Initialize with the first seen timestamp + results = {str(time): incidents for time, incidents in query.all()} + + distribution = [] + current_time = timestamp_filter.lower_timestamp.replace( + minute=0, second=0, microsecond=0 + ) + while current_time <= timestamp_filter.upper_timestamp: + timestamp_str = current_time.strftime(time_format) + distribution.append( + { + "timestamp": timestamp_str + ":00", + "number": results.get(timestamp_str, 0), } - else: - # Update the last alert timestamp if the current one is more recent - provider_distribution[provider_key]["last_alert_received"] = max( - provider_distribution[provider_key]["last_alert_received"], - last_alert_timestamp, + ) + current_time += timedelta(hours=1) + + return distribution + + +def calc_incidents_mttr(tenant_id: str, timestamp_filter: TimeStampFilter = None): + """ + Calculate the Mean Time to Resolve (MTTR) for incidents over time for a specific tenant. + + Args: + tenant_id (str): ID of the tenant whose incidents are being analyzed. + timestamp_filter (TimeStampFilter, optional): Filter to specify the time range. + - lower_timestamp (datetime): Start of the time range. + - upper_timestamp (datetime): End of the time range. + + Returns: + List[dict]: A list of dictionaries representing the hourly MTTR of incidents. + Each dictionary contains: + - 'timestamp' (str): Timestamp of the hour in "YYYY-MM-DD HH:00" format. + - 'mttr' (float): Mean Time to Resolve incidents in that hour (in hours). + + Notes: + - If no timestamp_filter is provided, defaults to the last 24 hours. + - Only includes resolved incidents. + - Supports MySQL, PostgreSQL, and SQLite for timestamp formatting. + """ + with Session(engine) as session: + twenty_four_hours_ago = datetime.utcnow() - timedelta(hours=24) + time_format = "%Y-%m-%d %H" + + filters = [ + Incident.tenant_id == tenant_id, + Incident.status == IncidentStatus.RESOLVED.value, + ] + if timestamp_filter: + if timestamp_filter.lower_timestamp: + filters.append( + Incident.creation_time >= timestamp_filter.lower_timestamp + ) + if timestamp_filter.upper_timestamp: + filters.append( + Incident.creation_time <= timestamp_filter.upper_timestamp ) + else: + filters.append(Incident.creation_time >= twenty_four_hours_ago) - time = datetime.strptime(time, time_format) - index = int((time - twenty_four_hours_ago).total_seconds() // 3600) + # Database-specific timestamp formatting + if session.bind.dialect.name == "mysql": + timestamp_format = func.date_format(Incident.creation_time, time_format) + elif session.bind.dialect.name == "postgresql": + timestamp_format = func.to_char(Incident.creation_time, "YYYY-MM-DD HH") + elif session.bind.dialect.name == "sqlite": + timestamp_format = func.strftime(time_format, Incident.creation_time) - if 0 <= index < 24: - provider_distribution[provider_key]["alert_last_24_hours"][index][ - "number" - ] += hits + query = ( + session.query( + timestamp_format.label("time"), + Incident.start_time, + Incident.end_time, + func.count().label("incidents"), + ) + .filter(*filters) + .group_by("time", Incident.start_time, Incident.end_time) + .order_by("time") + ) + results = {} + for time, start_time, end_time, incidents in query.all(): + if start_time and end_time: + resolution_time = ( + end_time - start_time + ).total_seconds() / 3600 # in hours + time_str = str(time) + if time_str not in results: + results[time_str] = {"number": 0, "mttr": 0} + + results[time_str]["number"] += incidents + results[time_str]["mttr"] += resolution_time * incidents + + distribution = [] + current_time = timestamp_filter.lower_timestamp.replace( + minute=0, second=0, microsecond=0 + ) + while current_time <= timestamp_filter.upper_timestamp: + timestamp_str = current_time.strftime(time_format) + if timestamp_str in results and results[timestamp_str]["number"] > 0: + avg_mttr = ( + results[timestamp_str]["mttr"] / results[timestamp_str]["number"] + ) + else: + avg_mttr = 0 - return provider_distribution + distribution.append( + { + "timestamp": timestamp_str + ":00", + "mttr": avg_mttr, + } + ) + current_time += timedelta(hours=1) + + return distribution def get_presets( @@ -2920,11 +3240,14 @@ def get_incident_alerts_and_links_by_incident_id( return query.all(), total_count + def get_incident_alerts_by_incident_id(*args, **kwargs) -> tuple[List[Alert], int]: """ Unpacking (List[(Alert, AlertToIncident)], int) to (List[Alert], int). """ - alerts_and_links, total_alerts = get_incident_alerts_and_links_by_incident_id(*args, **kwargs) + alerts_and_links, total_alerts = get_incident_alerts_and_links_by_incident_id( + *args, **kwargs + ) alerts = [alert_and_link[0] for alert_and_link in alerts_and_links] return alerts, total_alerts @@ -2950,21 +3273,20 @@ def get_future_incidents_by_incident_id( def get_all_same_alert_ids( - tenant_id: str, - alert_ids: List[str | UUID], - session: Optional[Session] = None + tenant_id: str, alert_ids: List[str | UUID], session: Optional[Session] = None ): with existed_or_new_session(session) as session: - fingerprints_subquery = session.query(Alert.fingerprint).where( - Alert.tenant_id == tenant_id, - col(Alert.id).in_(alert_ids) - ).subquery() + fingerprints_subquery = ( + session.query(Alert.fingerprint) + .where(Alert.tenant_id == tenant_id, col(Alert.id).in_(alert_ids)) + .subquery() + ) query = session.scalars( - select(Alert.id) - .where( - Alert.tenant_id == tenant_id, - col(Alert.fingerprint).in_(fingerprints_subquery) - )) + select(Alert.id).where( + Alert.tenant_id == tenant_id, + col(Alert.fingerprint).in_(fingerprints_subquery), + ) + ) return query.all() @@ -3042,7 +3364,9 @@ def add_alerts_to_incident_by_incident_id( if not incident: return None - return add_alerts_to_incident(tenant_id, incident, alert_ids, is_created_by_ai, session) + return add_alerts_to_incident( + tenant_id, incident, alert_ids, is_created_by_ai, session + ) def add_alerts_to_incident( @@ -3086,7 +3410,9 @@ def add_alerts_to_incident( ) new_alert_ids = [ - alert_id for alert_id in all_alert_ids if alert_id not in existing_alert_ids + alert_id + for alert_id in all_alert_ids + if alert_id not in existing_alert_ids ] if not new_alert_ids: @@ -3095,10 +3421,12 @@ def add_alerts_to_incident( alerts_data_for_incident = get_alerts_data_for_incident(new_alert_ids, existing_fingerprints, session) incident.sources = list( - set(incident.sources if incident.sources else []) | set(alerts_data_for_incident["sources"]) + set(incident.sources if incident.sources else []) + | set(alerts_data_for_incident["sources"]) ) incident.affected_services = list( - set(incident.affected_services if incident.affected_services else []) | set(alerts_data_for_incident["services"]) + set(incident.affected_services if incident.affected_services else []) + | set(alerts_data_for_incident["services"]) ) # If incident has alerts already, use the max severity between existing and new alerts, otherwise use the new alerts max severity incident.severity = max(incident.severity, alerts_data_for_incident["max_severity"].order) if incident.alerts_count else alerts_data_for_incident["max_severity"].order @@ -3106,7 +3434,10 @@ def add_alerts_to_incident( alert_to_incident_entries = [ AlertToIncident( - alert_id=alert_id, incident_id=incident.id, tenant_id=tenant_id, is_created_by_ai=is_created_by_ai + alert_id=alert_id, + incident_id=incident.id, + tenant_id=tenant_id, + is_created_by_ai=is_created_by_ai, ) for alert_id in new_alert_ids ] @@ -3206,9 +3537,12 @@ def remove_alerts_to_incident_by_incident_id( AlertToIncident.tenant_id == tenant_id, AlertToIncident.incident_id == incident.id, col(AlertToIncident.alert_id).in_(all_alert_ids), - ).update({ - "deleted_at": datetime.now(datetime.now().astimezone().tzinfo), - }) + ) + .update( + { + "deleted_at": datetime.now(datetime.now().astimezone().tzinfo), + } + ) ) session.commit() @@ -3658,7 +3992,10 @@ def get_alerts_fields(tenant_id: str) -> List[AlertField]: def change_incident_status_by_id( - tenant_id: str, incident_id: UUID | str, status: IncidentStatus + tenant_id: str, + incident_id: UUID | str, + status: IncidentStatus, + end_time: datetime | None = None, ) -> bool: with Session(engine) as session: stmt = ( @@ -3667,7 +4004,10 @@ def change_incident_status_by_id( Incident.tenant_id == tenant_id, Incident.id == incident_id, ) - .values(status=status.value) + .values( + status=status.value, + end_time=end_time, + ) ) updated = session.execute(stmt) session.commit() @@ -3720,7 +4060,7 @@ def get_workflow_executions_for_incident_or_alert( .join(AlertToIncident, Alert.id == AlertToIncident.alert_id) .where( AlertToIncident.deleted_at == NULL_FOR_DELETED_AT, - AlertToIncident.incident_id == incident_id + AlertToIncident.incident_id == incident_id, ) ) @@ -3830,37 +4170,35 @@ def is_edge_incident_alert_resolved( AlertEnrichment, Alert.fingerprint == AlertEnrichment.alert_fingerprint ) .join(AlertToIncident, AlertToIncident.alert_id == Alert.id) - .where( - AlertToIncident.incident_id == incident.id - ) + .where(AlertToIncident.incident_id == incident.id) .group_by(Alert.fingerprint) .having(func.max(Alert.timestamp)) .order_by(direction(Alert.timestamp)) ).first() - - return ( - enriched_status == AlertStatus.RESOLVED.value or - (enriched_status is None and status == AlertStatus.RESOLVED.value) + + return enriched_status == AlertStatus.RESOLVED.value or ( + enriched_status is None and status == AlertStatus.RESOLVED.value ) + def get_alerts_metrics_by_provider( tenant_id: str, - start_date: Optional[datetime] = None, + start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, - fields: Optional[List[str]] = [] + fields: Optional[List[str]] = [], ) -> Dict[str, Dict[str, Any]]: - + dynamic_field_sums = [ func.sum( case( [ ( - func.json_extract(Alert.event, f'$.{field}').isnot(None) & - (func.json_extract(Alert.event, f'$.{field}') != False), - 1 + func.json_extract(Alert.event, f"$.{field}").isnot(None) + & (func.json_extract(Alert.event, f"$.{field}") != False), + 1, ) - ], - else_=0 + ], + else_=0, ) ).label(f"{field}_count") for field in fields @@ -3872,8 +4210,10 @@ def get_alerts_metrics_by_provider( Alert.provider_type, Alert.provider_id, func.count(Alert.id).label("total_alerts"), - func.sum(case([(AlertToIncident.alert_id.isnot(None), 1)], else_=0)).label("correlated_alerts"), - *dynamic_field_sums + func.sum( + case([(AlertToIncident.alert_id.isnot(None), 1)], else_=0) + ).label("correlated_alerts"), + *dynamic_field_sums, ) .outerjoin(AlertToIncident, Alert.id == AlertToIncident.alert_id) .filter( @@ -3884,18 +4224,19 @@ def get_alerts_metrics_by_provider( # Add timestamp filter only if both start_date and end_date are provided if start_date and end_date: query = query.filter( - Alert.timestamp >= start_date, - Alert.timestamp <= end_date + Alert.timestamp >= start_date, Alert.timestamp <= end_date ) results = query.group_by(Alert.provider_id, Alert.provider_type).all() - + return { f"{row.provider_id}_{row.provider_type}": { "total_alerts": row.total_alerts, "correlated_alerts": row.correlated_alerts, "provider_type": row.provider_type, - **{f"{field}_count": getattr(row, f"{field}_count") for field in fields} # Add field-specific counts + **{ + f"{field}_count": getattr(row, f"{field}_count") for field in fields + }, # Add field-specific counts } for row in results } diff --git a/keep/api/models/alert.py b/keep/api/models/alert.py index c9de2b43f..8c3c59f3b 100644 --- a/keep/api/models/alert.py +++ b/keep/api/models/alert.py @@ -4,7 +4,7 @@ import logging import uuid from enum import Enum -from typing import Any, Dict, List, Optional, TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Dict, List, Optional from uuid import UUID import pytz @@ -14,7 +14,7 @@ Extra, PrivateAttr, root_validator, - validator + validator, ) from sqlalchemy import desc from sqlmodel import col @@ -310,20 +310,24 @@ class Config: "examples": [ { "id": "1234", - "name": "Alert name", + "name": "Pod 'api-service-production' lacks memory", "status": "firing", "lastReceived": "2021-01-01T00:00:00.000Z", "environment": "production", "duplicateReason": None, "service": "backend", - "source": ["keep"], - "message": "Keep: Alert message", - "description": "Keep: Alert description", + "source": ["prometheus"], + "message": "The pod 'api-service-production' lacks memory causing high error rate", + "description": "Due to the lack of memory, the pod 'api-service-production' is experiencing high error rate", "severity": "critical", "pushed": True, - "event_id": "1234", "url": "https://www.keephq.dev?alertId=1234", - "labels": {"key": "value"}, + "labels": { + "pod": "api-service-production", + "region": "us-east-1", + "cpu": "88", + "memory": "100Mi", + }, "ticket_url": "https://www.keephq.dev?enrichedTicketId=456", "fingerprint": "1234", } diff --git a/keep/api/models/time_stamp.py b/keep/api/models/time_stamp.py index 67c910538..afe9b0c13 100644 --- a/keep/api/models/time_stamp.py +++ b/keep/api/models/time_stamp.py @@ -1,10 +1,27 @@ +import json from typing import Optional + +from fastapi import Query, HTTPException from pydantic import BaseModel, Field from datetime import datetime + + class TimeStampFilter(BaseModel): - lower_timestamp: Optional[datetime] = Field(None, alias='start') - upper_timestamp: Optional[datetime] = Field(None, alias='end') + lower_timestamp: Optional[datetime] = Field(None, alias="start") + upper_timestamp: Optional[datetime] = Field(None, alias="end") class Config: allow_population_by_field_name = True - \ No newline at end of file + + +# Function to handle the time_stamp query parameter and parse it +def _get_time_stamp_filter(time_stamp: Optional[str] = Query(None)) -> TimeStampFilter: + if time_stamp: + try: + # Parse the JSON string + time_stamp_dict = json.loads(time_stamp) + # Return the TimeStampFilter object, Pydantic will map 'from' -> lower_timestamp and 'to' -> upper_timestamp + return TimeStampFilter(**time_stamp_dict) + except (json.JSONDecodeError, TypeError): + raise HTTPException(status_code=400, detail="Invalid time_stamp format") + return TimeStampFilter() diff --git a/keep/api/routes/dashboard.py b/keep/api/routes/dashboard.py index 32e437947..cfcb61d4a 100644 --- a/keep/api/routes/dashboard.py +++ b/keep/api/routes/dashboard.py @@ -1,16 +1,23 @@ import json import logging import os -from datetime import datetime +from datetime import datetime, timedelta from typing import Dict, List, Optional from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel -from keep.api.core.db import create_dashboard as create_dashboard_db +from keep.api.core.db import ( + create_dashboard as create_dashboard_db, + get_provider_distribution, + get_incidents_created_distribution, + get_combined_workflow_execution_distribution, + calc_incidents_mttr, +) from keep.api.core.db import delete_dashboard as delete_dashboard_db from keep.api.core.db import get_dashboards as get_dashboards_db from keep.api.core.db import update_dashboard as update_dashboard_db +from keep.api.models.time_stamp import TimeStampFilter, _get_time_stamp_filter from keep.identitymanager.authenticatedentity import AuthenticatedEntity from keep.identitymanager.identitymanagerfactory import IdentityManagerFactory @@ -136,3 +143,40 @@ def delete_dashboard( if not dashboard: raise HTTPException(status_code=404, detail="Dashboard not found") return {"ok": True} + + +@router.get("/metric-widgets") +def get_metric_widgets( + time_stamp: TimeStampFilter = Depends(_get_time_stamp_filter), + mttr: bool = True, + apd: bool = True, + ipd: bool = True, + wpd: bool = True, + authenticated_entity: AuthenticatedEntity = Depends( + IdentityManagerFactory.get_auth_verifier(["read:dashboards"]) + ), +): + data = {} + tenant_id = authenticated_entity.tenant_id + if not time_stamp.lower_timestamp or not time_stamp.upper_timestamp: + time_stamp = TimeStampFilter( + upper_timestamp=datetime.utcnow(), + lower_timestamp=datetime.utcnow() - timedelta(hours=24), + ) + if apd: + data["apd"] = get_provider_distribution( + tenant_id=tenant_id, aggregate_all=True, timestamp_filter=time_stamp + ) + if ipd: + data["ipd"] = get_incidents_created_distribution( + tenant_id=tenant_id, timestamp_filter=time_stamp + ) + if wpd: + data["wpd"] = get_combined_workflow_execution_distribution( + tenant_id=tenant_id, timestamp_filter=time_stamp + ) + if mttr: + data["mttr"] = calc_incidents_mttr( + tenant_id=tenant_id, timestamp_filter=time_stamp + ) + return data diff --git a/keep/api/routes/incidents.py b/keep/api/routes/incidents.py index ed87d105a..f46c855c2 100644 --- a/keep/api/routes/incidents.py +++ b/keep/api/routes/incidents.py @@ -569,7 +569,9 @@ async def add_alerts_to_incident( if not incident: raise HTTPException(status_code=404, detail="Incident not found") - add_alerts_to_incident_by_incident_id(tenant_id, incident_id, alert_ids, is_created_by_ai) + add_alerts_to_incident_by_incident_id( + tenant_id, incident_id, alert_ids, is_created_by_ai + ) try: logger.info("Pushing enriched alert to elasticsearch") elastic_client = ElasticClient(tenant_id) @@ -762,7 +764,10 @@ def change_incident_status( # We need to do something only if status really changed if not change.status == incident.status: - result = change_incident_status_by_id(tenant_id, incident_id, change.status) + end_time = datetime.utcnow() if change.status == IncidentStatus.RESOLVED else None + result = change_incident_status_by_id( + tenant_id, incident_id, change.status, end_time + ) if not result: raise HTTPException( status_code=500, detail="Error changing incident status" @@ -778,7 +783,7 @@ def change_incident_status( ), authenticated_entity=authenticated_entity, ) - + incident.end_time = end_time incident.status = change.status new_incident_dto = IncidentDto.from_db_incident(incident) diff --git a/keep/api/routes/preset.py b/keep/api/routes/preset.py index cfa701681..a4f87cc08 100644 --- a/keep/api/routes/preset.py +++ b/keep/api/routes/preset.py @@ -1,16 +1,13 @@ -import json import logging import os import uuid from datetime import datetime -from typing import Optional from fastapi import ( APIRouter, BackgroundTasks, Depends, HTTPException, - Query, Request, Response, ) @@ -34,7 +31,7 @@ Tag, TagDto, ) -from keep.api.models.time_stamp import TimeStampFilter +from keep.api.models.time_stamp import TimeStampFilter, _get_time_stamp_filter from keep.api.tasks.process_event_task import process_event from keep.api.tasks.process_topology_task import process_topology from keep.contextmanager.contextmanager import ContextManager @@ -177,19 +174,6 @@ def pull_data_from_providers( ) -# Function to handle the time_stamp query parameter and parse it -def _get_time_stamp_filter(time_stamp: Optional[str] = Query(None)) -> TimeStampFilter: - if time_stamp: - try: - # Parse the JSON string - time_stamp_dict = json.loads(time_stamp) - # Return the TimeStampFilter object, Pydantic will map 'from' -> lower_timestamp and 'to' -> upper_timestamp - return TimeStampFilter(**time_stamp_dict) - except (json.JSONDecodeError, TypeError): - raise HTTPException(status_code=400, detail="Invalid time_stamp format") - return TimeStampFilter() - - @router.get( "", description="Get all presets for tenant",