mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-08 22:48:14 -05:00
fix(import): fix array errors on import/export (#2211)
* Fix import/export * Remove copilot gdrive tools * Null * Fix lint * Add copilot validation * Fix validation
This commit is contained in:
committed by
GitHub
parent
7101dc58d4
commit
7752beac01
@@ -1,158 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { AlertCircle } from 'lucide-react'
|
||||
import mermaid from 'mermaid'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('MermaidDiagram')
|
||||
|
||||
interface MermaidDiagramProps {
|
||||
diagramText: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders mermaid diagrams with pan/zoom support
|
||||
*/
|
||||
export function MermaidDiagram({ diagramText }: MermaidDiagramProps) {
|
||||
const [dataUrl, setDataUrl] = useState<string | null>(null)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [zoom, setZoom] = useState(0.6)
|
||||
const [pan, setPan] = useState({ x: 0, y: 0 })
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const dragStart = useRef({ x: 0, y: 0, panX: 0, panY: 0 })
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const renderDiagram = useCallback(async () => {
|
||||
if (!diagramText?.trim()) {
|
||||
setError('No diagram text provided')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setError(null)
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'base',
|
||||
securityLevel: 'loose',
|
||||
fontFamily: 'system-ui, -apple-system, sans-serif',
|
||||
fontSize: 14,
|
||||
flowchart: {
|
||||
useMaxWidth: false,
|
||||
htmlLabels: true,
|
||||
padding: 20,
|
||||
nodeSpacing: 50,
|
||||
rankSpacing: 60,
|
||||
},
|
||||
themeVariables: {
|
||||
primaryColor: '#dbeafe',
|
||||
primaryTextColor: '#1e3a5f',
|
||||
primaryBorderColor: '#3b82f6',
|
||||
lineColor: '#64748b',
|
||||
secondaryColor: '#fef3c7',
|
||||
secondaryTextColor: '#92400e',
|
||||
secondaryBorderColor: '#f59e0b',
|
||||
tertiaryColor: '#d1fae5',
|
||||
tertiaryTextColor: '#065f46',
|
||||
tertiaryBorderColor: '#10b981',
|
||||
background: '#ffffff',
|
||||
mainBkg: '#dbeafe',
|
||||
nodeBorder: '#3b82f6',
|
||||
nodeTextColor: '#1e3a5f',
|
||||
clusterBkg: '#f1f5f9',
|
||||
clusterBorder: '#94a3b8',
|
||||
titleColor: '#0f172a',
|
||||
textColor: '#334155',
|
||||
edgeLabelBackground: '#ffffff',
|
||||
},
|
||||
})
|
||||
|
||||
const id = `mermaid-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
||||
// Replace \n with <br> for proper line breaks in labels
|
||||
const processedText = diagramText.trim().replace(/\\n/g, '<br>')
|
||||
const { svg } = await mermaid.render(id, processedText)
|
||||
const encoded = btoa(unescape(encodeURIComponent(svg)))
|
||||
setDataUrl(`data:image/svg+xml;base64,${encoded}`)
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : 'Failed to render diagram'
|
||||
logger.error('Mermaid render error', { error: msg })
|
||||
setError(msg)
|
||||
}
|
||||
}, [diagramText])
|
||||
|
||||
useEffect(() => {
|
||||
renderDiagram()
|
||||
}, [renderDiagram])
|
||||
|
||||
const handleWheel = useCallback((e: React.WheelEvent) => {
|
||||
e.preventDefault()
|
||||
const delta = e.deltaY > 0 ? 0.9 : 1.1
|
||||
setZoom((z) => Math.min(Math.max(z * delta, 0.1), 3))
|
||||
}, [])
|
||||
|
||||
const handleMouseDown = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
setIsDragging(true)
|
||||
dragStart.current = { x: e.clientX, y: e.clientY, panX: pan.x, panY: pan.y }
|
||||
},
|
||||
[pan]
|
||||
)
|
||||
|
||||
const handleMouseMove = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
if (!isDragging) return
|
||||
setPan({
|
||||
x: dragStart.current.panX + (e.clientX - dragStart.current.x),
|
||||
y: dragStart.current.panY + (e.clientY - dragStart.current.y),
|
||||
})
|
||||
},
|
||||
[isDragging]
|
||||
)
|
||||
|
||||
const handleMouseUp = useCallback(() => setIsDragging(false), [])
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className='flex items-center gap-2 rounded-md border border-red-500/30 bg-red-500/10 p-3 text-red-400 text-sm'>
|
||||
<AlertCircle className='h-4 w-4 flex-shrink-0' />
|
||||
<span>{error}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!dataUrl) {
|
||||
return (
|
||||
<div className='flex h-24 items-center justify-center text-[var(--text-tertiary)] text-sm'>
|
||||
Rendering...
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className='select-none overflow-hidden rounded-md border border-[var(--border-strong)] bg-white'
|
||||
style={{
|
||||
height: 500,
|
||||
minHeight: 150,
|
||||
resize: 'vertical',
|
||||
cursor: isDragging ? 'grabbing' : 'grab',
|
||||
}}
|
||||
onWheel={handleWheel}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseLeave={handleMouseUp}
|
||||
title='Scroll to zoom, drag to pan, drag edge to resize'
|
||||
>
|
||||
<img
|
||||
src={dataUrl}
|
||||
alt='Mermaid diagram'
|
||||
className='pointer-events-none h-full w-full object-contain'
|
||||
style={{ transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})` }}
|
||||
draggable={false}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,16 +2,12 @@
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import useDrivePicker from 'react-google-drive-picker'
|
||||
import { Button, Code } from '@/components/emcn'
|
||||
import { GoogleDriveIcon } from '@/components/icons'
|
||||
import { ClientToolCallState } from '@/lib/copilot/tools/client/base-tool'
|
||||
import { getClientTool } from '@/lib/copilot/tools/client/manager'
|
||||
import { getRegisteredTools } from '@/lib/copilot/tools/client/registry'
|
||||
import { getEnv } from '@/lib/core/config/env'
|
||||
import { CLASS_TOOL_METADATA, useCopilotStore } from '@/stores/panel/copilot/store'
|
||||
import type { CopilotToolCall } from '@/stores/panel/copilot/types'
|
||||
import { MermaidDiagram } from '../mermaid-diagram/mermaid-diagram'
|
||||
|
||||
interface ToolCallProps {
|
||||
toolCall?: CopilotToolCall
|
||||
@@ -351,7 +347,6 @@ function RunSkipButtons({
|
||||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
const [buttonsHidden, setButtonsHidden] = useState(false)
|
||||
const { setToolCallState } = useCopilotStore()
|
||||
const [openPicker] = useDrivePicker()
|
||||
|
||||
const instance = getClientTool(toolCall.id)
|
||||
const interruptDisplays = instance?.getInterruptDisplays?.()
|
||||
@@ -368,107 +363,8 @@ function RunSkipButtons({
|
||||
}
|
||||
}
|
||||
|
||||
// const handleOpenDriveAccess = async () => {
|
||||
// try {
|
||||
// const providerId = 'google-drive'
|
||||
// const credsRes = await fetch(`/api/auth/oauth/credentials?provider=${providerId}`)
|
||||
// if (!credsRes.ok) return
|
||||
// const credsData = await credsRes.json()
|
||||
// const creds = Array.isArray(credsData.credentials) ? credsData.credentials : []
|
||||
// if (creds.length === 0) return
|
||||
// const defaultCred = creds.find((c: any) => c.isDefault) || creds[0]
|
||||
|
||||
// const tokenRes = await fetch('/api/auth/oauth/token', {
|
||||
// method: 'POST',
|
||||
// headers: { 'Content-Type': 'application/json' },
|
||||
// body: JSON.stringify({ credentialId: defaultCred.id }),
|
||||
// })
|
||||
// if (!tokenRes.ok) return
|
||||
// const { accessToken } = await tokenRes.json()
|
||||
// if (!accessToken) return
|
||||
|
||||
// const clientId = getEnv('NEXT_PUBLIC_GOOGLE_CLIENT_ID') || ''
|
||||
// const apiKey = getEnv('NEXT_PUBLIC_GOOGLE_API_KEY') || ''
|
||||
// const projectNumber = getEnv('NEXT_PUBLIC_GOOGLE_PROJECT_NUMBER') || ''
|
||||
|
||||
// openPicker({
|
||||
// clientId,
|
||||
// developerKey: apiKey,
|
||||
// viewId: 'DOCS',
|
||||
// token: accessToken,
|
||||
// showUploadView: true,
|
||||
// showUploadFolders: true,
|
||||
// supportDrives: true,
|
||||
// multiselect: false,
|
||||
// appId: projectNumber,
|
||||
// setSelectFolderEnabled: false,
|
||||
// callbackFunction: async (data) => {
|
||||
// if (data.action === 'picked') {
|
||||
// await onRun()
|
||||
// }
|
||||
// },
|
||||
// })
|
||||
// } catch {}
|
||||
// }
|
||||
|
||||
if (buttonsHidden) return null
|
||||
|
||||
if (toolCall.name === 'gdrive_request_access' && toolCall.state === 'pending') {
|
||||
return (
|
||||
<div className='mt-[10px] flex gap-[6px]'>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const instance = getClientTool(toolCall.id)
|
||||
if (!instance) return
|
||||
await instance.handleAccept?.({
|
||||
openDrivePicker: async (accessToken: string) => {
|
||||
try {
|
||||
const clientId = getEnv('NEXT_PUBLIC_GOOGLE_CLIENT_ID') || ''
|
||||
const apiKey = getEnv('NEXT_PUBLIC_GOOGLE_API_KEY') || ''
|
||||
const projectNumber = getEnv('NEXT_PUBLIC_GOOGLE_PROJECT_NUMBER') || ''
|
||||
return await new Promise<boolean>((resolve) => {
|
||||
openPicker({
|
||||
clientId,
|
||||
developerKey: apiKey,
|
||||
viewId: 'DOCS',
|
||||
token: accessToken,
|
||||
showUploadView: true,
|
||||
showUploadFolders: true,
|
||||
supportDrives: true,
|
||||
multiselect: false,
|
||||
appId: projectNumber,
|
||||
setSelectFolderEnabled: false,
|
||||
callbackFunction: async (data) => {
|
||||
if (data.action === 'picked') resolve(true)
|
||||
else if (data.action === 'cancel') resolve(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
})
|
||||
}}
|
||||
variant='primary'
|
||||
title='Grant Google Drive access'
|
||||
>
|
||||
<GoogleDriveIcon className='mr-0.5 h-4 w-4' />
|
||||
Select
|
||||
</Button>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
setButtonsHidden(true)
|
||||
await handleSkip(toolCall, setToolCallState, onStateChange)
|
||||
}}
|
||||
variant='default'
|
||||
>
|
||||
Skip
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-[12px] flex gap-[6px]'>
|
||||
<Button onClick={onRun} disabled={isProcessing} variant='primary'>
|
||||
@@ -950,35 +846,6 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
|
||||
)
|
||||
}
|
||||
|
||||
// Special rendering for generate_diagram - show mermaid diagram
|
||||
if (toolCall.name === 'generate_diagram') {
|
||||
const diagramText = params.diagramText || ''
|
||||
const language = params.language || 'mermaid'
|
||||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
<ShimmerOverlayText
|
||||
text={displayName}
|
||||
active={isLoadingState}
|
||||
isSpecial={false}
|
||||
className='font-[470] font-season text-[#939393] text-sm dark:text-[#939393]'
|
||||
/>
|
||||
{diagramText && language === 'mermaid' && (
|
||||
<div className='mt-2'>
|
||||
<MermaidDiagram diagramText={diagramText} />
|
||||
</div>
|
||||
)}
|
||||
{showButtons && (
|
||||
<RunSkipButtons
|
||||
toolCall={toolCall}
|
||||
onStateChange={handleStateChange}
|
||||
editedParams={editedParams}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
<div
|
||||
|
||||
@@ -20,13 +20,10 @@ export const ToolIds = z.enum([
|
||||
'make_api_request',
|
||||
'set_environment_variables',
|
||||
'get_credentials',
|
||||
'gdrive_request_access',
|
||||
'list_gdrive_files',
|
||||
'read_gdrive_file',
|
||||
'reason',
|
||||
'list_user_workflows',
|
||||
'get_workflow_from_name',
|
||||
'get_global_workflow_variables',
|
||||
'get_workflow_data',
|
||||
'set_global_workflow_variables',
|
||||
'oauth_request_access',
|
||||
'get_trigger_blocks',
|
||||
@@ -34,7 +31,6 @@ export const ToolIds = z.enum([
|
||||
'check_deployment_status',
|
||||
'navigate_ui',
|
||||
'knowledge_base',
|
||||
'generate_diagram',
|
||||
])
|
||||
export type ToolId = z.infer<typeof ToolIds>
|
||||
|
||||
@@ -61,8 +57,10 @@ export const ToolArgSchemas = {
|
||||
// New tools
|
||||
list_user_workflows: z.object({}),
|
||||
get_workflow_from_name: z.object({ workflow_name: z.string() }),
|
||||
// New variable tools
|
||||
get_global_workflow_variables: z.object({}),
|
||||
// Workflow data tool (variables, custom tools, MCP tools, files)
|
||||
get_workflow_data: z.object({
|
||||
data_type: z.enum(['global_variables', 'custom_tools', 'mcp_tools', 'files']),
|
||||
}),
|
||||
set_global_workflow_variables: z.object({
|
||||
operations: z.array(
|
||||
z.object({
|
||||
@@ -184,29 +182,11 @@ export const ToolArgSchemas = {
|
||||
|
||||
get_credentials: z.object({}),
|
||||
|
||||
gdrive_request_access: z.object({}),
|
||||
|
||||
list_gdrive_files: z.object({
|
||||
search_query: z.string().optional(),
|
||||
num_results: z.number().optional().default(50),
|
||||
}),
|
||||
|
||||
read_gdrive_file: z.object({
|
||||
fileId: z.string(),
|
||||
type: z.enum(['doc', 'sheet']),
|
||||
range: z.string().optional(),
|
||||
}),
|
||||
|
||||
reason: z.object({
|
||||
reasoning: z.string(),
|
||||
}),
|
||||
|
||||
knowledge_base: KnowledgeBaseArgsSchema,
|
||||
|
||||
generate_diagram: z.object({
|
||||
diagramText: z.string().describe('The raw diagram text content (e.g., mermaid syntax)'),
|
||||
language: z.enum(['mermaid']).default('mermaid').describe('The diagram language/format'),
|
||||
}),
|
||||
} as const
|
||||
export type ToolArgSchemaMap = typeof ToolArgSchemas
|
||||
|
||||
@@ -231,11 +211,8 @@ export const ToolSSESchemas = {
|
||||
'get_workflow_from_name',
|
||||
ToolArgSchemas.get_workflow_from_name
|
||||
),
|
||||
// New variable tools
|
||||
get_global_workflow_variables: toolCallSSEFor(
|
||||
'get_global_workflow_variables',
|
||||
ToolArgSchemas.get_global_workflow_variables
|
||||
),
|
||||
// Workflow data tool (variables, custom tools, MCP tools, files)
|
||||
get_workflow_data: toolCallSSEFor('get_workflow_data', ToolArgSchemas.get_workflow_data),
|
||||
set_global_workflow_variables: toolCallSSEFor(
|
||||
'set_global_workflow_variables',
|
||||
ToolArgSchemas.set_global_workflow_variables
|
||||
@@ -264,12 +241,6 @@ export const ToolSSESchemas = {
|
||||
ToolArgSchemas.set_environment_variables
|
||||
),
|
||||
get_credentials: toolCallSSEFor('get_credentials', ToolArgSchemas.get_credentials),
|
||||
gdrive_request_access: toolCallSSEFor(
|
||||
'gdrive_request_access',
|
||||
ToolArgSchemas.gdrive_request_access as any
|
||||
),
|
||||
list_gdrive_files: toolCallSSEFor('list_gdrive_files', ToolArgSchemas.list_gdrive_files),
|
||||
read_gdrive_file: toolCallSSEFor('read_gdrive_file', ToolArgSchemas.read_gdrive_file),
|
||||
reason: toolCallSSEFor('reason', ToolArgSchemas.reason),
|
||||
// New
|
||||
oauth_request_access: toolCallSSEFor('oauth_request_access', ToolArgSchemas.oauth_request_access),
|
||||
@@ -280,7 +251,6 @@ export const ToolSSESchemas = {
|
||||
),
|
||||
navigate_ui: toolCallSSEFor('navigate_ui', ToolArgSchemas.navigate_ui),
|
||||
knowledge_base: toolCallSSEFor('knowledge_base', ToolArgSchemas.knowledge_base),
|
||||
generate_diagram: toolCallSSEFor('generate_diagram', ToolArgSchemas.generate_diagram),
|
||||
} as const
|
||||
export type ToolSSESchemaMap = typeof ToolSSESchemas
|
||||
|
||||
@@ -328,10 +298,47 @@ export const ToolResultSchemas = {
|
||||
.object({ yamlContent: z.string() })
|
||||
.or(z.object({ userWorkflow: z.string() }))
|
||||
.or(z.string()),
|
||||
// New variable tools
|
||||
get_global_workflow_variables: z
|
||||
.object({ variables: z.record(z.any()) })
|
||||
.or(z.array(z.object({ name: z.string(), value: z.any() }))),
|
||||
// Workflow data tool results (variables, custom tools, MCP tools, files)
|
||||
get_workflow_data: z.union([
|
||||
z.object({
|
||||
variables: z.array(z.object({ id: z.string(), name: z.string(), value: z.any() })),
|
||||
}),
|
||||
z.object({
|
||||
customTools: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
functionName: z.string(),
|
||||
description: z.string(),
|
||||
parameters: z.any().optional(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
z.object({
|
||||
mcpTools: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
serverId: z.string(),
|
||||
serverName: z.string(),
|
||||
description: z.string(),
|
||||
inputSchema: z.any().optional(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
z.object({
|
||||
files: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
key: z.string(),
|
||||
path: z.string(),
|
||||
size: z.number(),
|
||||
type: z.string(),
|
||||
uploadedAt: z.string(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
]),
|
||||
set_global_workflow_variables: z
|
||||
.object({ variables: z.record(z.any()) })
|
||||
.or(z.object({ message: z.any().optional(), data: z.any().optional() })),
|
||||
@@ -438,21 +445,6 @@ export const ToolResultSchemas = {
|
||||
count: z.number(),
|
||||
}),
|
||||
}),
|
||||
gdrive_request_access: z.object({
|
||||
granted: z.boolean().optional(),
|
||||
message: z.string().optional(),
|
||||
}),
|
||||
list_gdrive_files: z.object({
|
||||
files: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string().optional(),
|
||||
mimeType: z.string().optional(),
|
||||
size: z.number().optional(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
read_gdrive_file: z.object({ content: z.string().optional(), data: z.any().optional() }),
|
||||
reason: z.object({ reasoning: z.string() }),
|
||||
deploy_workflow: z.object({
|
||||
action: z.enum(['deploy', 'undeploy']).optional(),
|
||||
@@ -479,11 +471,6 @@ export const ToolResultSchemas = {
|
||||
navigated: z.boolean(),
|
||||
}),
|
||||
knowledge_base: KnowledgeBaseResultSchema,
|
||||
generate_diagram: z.object({
|
||||
diagramText: z.string(),
|
||||
language: z.enum(['mermaid']),
|
||||
rendered: z.boolean().optional(),
|
||||
}),
|
||||
} as const
|
||||
export type ToolResultSchemaMap = typeof ToolResultSchemas
|
||||
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import { FolderOpen, Loader2, MinusCircle, XCircle } from 'lucide-react'
|
||||
import {
|
||||
BaseClientTool,
|
||||
type BaseClientToolMetadata,
|
||||
ClientToolCallState,
|
||||
} from '@/lib/copilot/tools/client/base-tool'
|
||||
import { ExecuteResponseSuccessSchema } from '@/lib/copilot/tools/shared/schemas'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
interface ListGDriveFilesArgs {
|
||||
userId?: string
|
||||
workflowId?: string
|
||||
search_query?: string
|
||||
searchQuery?: string
|
||||
num_results?: number
|
||||
}
|
||||
|
||||
export class ListGDriveFilesClientTool extends BaseClientTool {
|
||||
static readonly id = 'list_gdrive_files'
|
||||
|
||||
constructor(toolCallId: string) {
|
||||
super(toolCallId, ListGDriveFilesClientTool.id, ListGDriveFilesClientTool.metadata)
|
||||
}
|
||||
|
||||
static readonly metadata: BaseClientToolMetadata = {
|
||||
displayNames: {
|
||||
[ClientToolCallState.generating]: { text: 'Listing GDrive files', icon: Loader2 },
|
||||
[ClientToolCallState.pending]: { text: 'Listing GDrive files', icon: Loader2 },
|
||||
[ClientToolCallState.executing]: { text: 'Listing GDrive files', icon: Loader2 },
|
||||
[ClientToolCallState.success]: { text: 'Listed GDrive files', icon: FolderOpen },
|
||||
[ClientToolCallState.error]: { text: 'Failed to list GDrive files', icon: XCircle },
|
||||
[ClientToolCallState.rejected]: { text: 'Skipped listing GDrive files', icon: MinusCircle },
|
||||
},
|
||||
getDynamicText: (params, state) => {
|
||||
const searchQuery = params?.search_query || params?.searchQuery
|
||||
if (searchQuery && typeof searchQuery === 'string') {
|
||||
const query = searchQuery
|
||||
const truncated = query.length > 40 ? `${query.slice(0, 40)}...` : query
|
||||
|
||||
switch (state) {
|
||||
case ClientToolCallState.success:
|
||||
return `Listed files matching ${truncated}`
|
||||
case ClientToolCallState.executing:
|
||||
case ClientToolCallState.generating:
|
||||
case ClientToolCallState.pending:
|
||||
return `Listing files matching ${truncated}`
|
||||
case ClientToolCallState.error:
|
||||
return `Failed to list files matching ${truncated}`
|
||||
case ClientToolCallState.rejected:
|
||||
return `Skipped listing files matching ${truncated}`
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
}
|
||||
|
||||
async execute(args?: ListGDriveFilesArgs): Promise<void> {
|
||||
const logger = createLogger('ListGDriveFilesClientTool')
|
||||
try {
|
||||
this.setState(ClientToolCallState.executing)
|
||||
|
||||
// Ensure server can resolve userId via workflowId if userId not provided
|
||||
const payload: ListGDriveFilesArgs = { ...(args || {}) }
|
||||
if (!payload.userId && !payload.workflowId) {
|
||||
const { activeWorkflowId } = useWorkflowRegistry.getState()
|
||||
if (activeWorkflowId) payload.workflowId = activeWorkflowId
|
||||
}
|
||||
|
||||
const res = await fetch('/api/copilot/execute-copilot-server-tool', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ toolName: 'list_gdrive_files', payload }),
|
||||
})
|
||||
if (!res.ok) {
|
||||
const txt = await res.text().catch(() => '')
|
||||
throw new Error(txt || `Server error (${res.status})`)
|
||||
}
|
||||
const json = await res.json()
|
||||
const parsed = ExecuteResponseSuccessSchema.parse(json)
|
||||
this.setState(ClientToolCallState.success)
|
||||
await this.markToolComplete(200, 'Listed Google Drive files', parsed.result)
|
||||
this.setState(ClientToolCallState.success)
|
||||
} catch (e: any) {
|
||||
logger.error('execute failed', { message: e?.message })
|
||||
this.setState(ClientToolCallState.error)
|
||||
await this.markToolComplete(500, e?.message || 'Failed to list Google Drive files')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { FileText, Loader2, MinusCircle, XCircle } from 'lucide-react'
|
||||
import {
|
||||
BaseClientTool,
|
||||
type BaseClientToolMetadata,
|
||||
ClientToolCallState,
|
||||
} from '@/lib/copilot/tools/client/base-tool'
|
||||
import { ExecuteResponseSuccessSchema } from '@/lib/copilot/tools/shared/schemas'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
interface ReadGDriveFileArgs {
|
||||
userId: string
|
||||
fileId: string
|
||||
type: 'doc' | 'sheet'
|
||||
range?: string
|
||||
}
|
||||
|
||||
export class ReadGDriveFileClientTool extends BaseClientTool {
|
||||
static readonly id = 'read_gdrive_file'
|
||||
|
||||
constructor(toolCallId: string) {
|
||||
super(toolCallId, ReadGDriveFileClientTool.id, ReadGDriveFileClientTool.metadata)
|
||||
}
|
||||
|
||||
static readonly metadata: BaseClientToolMetadata = {
|
||||
displayNames: {
|
||||
[ClientToolCallState.generating]: { text: 'Reading Google Drive file', icon: Loader2 },
|
||||
[ClientToolCallState.pending]: { text: 'Reading Google Drive file', icon: Loader2 },
|
||||
[ClientToolCallState.executing]: { text: 'Reading Google Drive file', icon: Loader2 },
|
||||
[ClientToolCallState.success]: { text: 'Read Google Drive file', icon: FileText },
|
||||
[ClientToolCallState.error]: { text: 'Failed to read Google Drive file', icon: XCircle },
|
||||
[ClientToolCallState.aborted]: { text: 'Aborted reading Google Drive file', icon: XCircle },
|
||||
[ClientToolCallState.rejected]: {
|
||||
text: 'Skipped reading Google Drive file',
|
||||
icon: MinusCircle,
|
||||
},
|
||||
},
|
||||
getDynamicText: (params, state) => {
|
||||
if (params?.fileId && typeof params.fileId === 'string') {
|
||||
const fileId = params.fileId
|
||||
const fileType = params?.type ? ` (${params.type})` : ''
|
||||
|
||||
switch (state) {
|
||||
case ClientToolCallState.success:
|
||||
return `Read file ${fileId}${fileType}`
|
||||
case ClientToolCallState.executing:
|
||||
case ClientToolCallState.generating:
|
||||
case ClientToolCallState.pending:
|
||||
return `Reading file ${fileId}${fileType}`
|
||||
case ClientToolCallState.error:
|
||||
return `Failed to read file ${fileId}${fileType}`
|
||||
case ClientToolCallState.aborted:
|
||||
return `Aborted reading file ${fileId}${fileType}`
|
||||
case ClientToolCallState.rejected:
|
||||
return `Skipped reading file ${fileId}${fileType}`
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
}
|
||||
|
||||
async execute(args?: ReadGDriveFileArgs): Promise<void> {
|
||||
const logger = createLogger('ReadGDriveFileClientTool')
|
||||
try {
|
||||
this.setState(ClientToolCallState.executing)
|
||||
const res = await fetch('/api/copilot/execute-copilot-server-tool', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ toolName: 'read_gdrive_file', payload: args || {} }),
|
||||
})
|
||||
if (!res.ok) {
|
||||
const txt = await res.text().catch(() => '')
|
||||
throw new Error(txt || `Server error (${res.status})`)
|
||||
}
|
||||
const json = await res.json()
|
||||
const parsed = ExecuteResponseSuccessSchema.parse(json)
|
||||
this.setState(ClientToolCallState.success)
|
||||
await this.markToolComplete(200, 'Read Google Drive file', parsed.result)
|
||||
this.setState(ClientToolCallState.success)
|
||||
} catch (e: any) {
|
||||
logger.error('execute failed', { message: e?.message })
|
||||
this.setState(ClientToolCallState.error)
|
||||
await this.markToolComplete(500, e?.message || 'Failed to read Google Drive file')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import { CheckCircle, FolderOpen, Loader2, MinusCircle, X, XCircle } from 'lucide-react'
|
||||
import {
|
||||
BaseClientTool,
|
||||
type BaseClientToolMetadata,
|
||||
ClientToolCallState,
|
||||
} from '@/lib/copilot/tools/client/base-tool'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
interface GDriveAcceptContext {
|
||||
openDrivePicker: (accessToken: string) => Promise<boolean>
|
||||
}
|
||||
|
||||
export class GDriveRequestAccessClientTool extends BaseClientTool {
|
||||
static readonly id = 'gdrive_request_access'
|
||||
|
||||
constructor(toolCallId: string) {
|
||||
super(toolCallId, GDriveRequestAccessClientTool.id, GDriveRequestAccessClientTool.metadata)
|
||||
}
|
||||
|
||||
static readonly metadata: BaseClientToolMetadata = {
|
||||
displayNames: {
|
||||
[ClientToolCallState.generating]: { text: 'Requesting GDrive access', icon: Loader2 },
|
||||
[ClientToolCallState.pending]: { text: 'Requesting GDrive access', icon: Loader2 },
|
||||
[ClientToolCallState.executing]: { text: 'Requesting GDrive access', icon: Loader2 },
|
||||
[ClientToolCallState.rejected]: { text: 'Skipped GDrive access', icon: MinusCircle },
|
||||
[ClientToolCallState.success]: { text: 'GDrive access granted', icon: CheckCircle },
|
||||
[ClientToolCallState.error]: { text: 'Failed to request GDrive access', icon: X },
|
||||
[ClientToolCallState.aborted]: { text: 'Aborted GDrive access request', icon: XCircle },
|
||||
},
|
||||
interrupt: {
|
||||
accept: { text: 'Select', icon: FolderOpen },
|
||||
reject: { text: 'Skip', icon: MinusCircle },
|
||||
},
|
||||
}
|
||||
|
||||
// Accept flow: fetch creds/token, then call provided openDrivePicker to get grant
|
||||
async handleAccept(ctx?: GDriveAcceptContext): Promise<void> {
|
||||
const logger = createLogger('GDriveRequestAccessClientTool')
|
||||
logger.debug('handleAccept() called', { toolCallId: this.toolCallId })
|
||||
|
||||
if (!ctx?.openDrivePicker) {
|
||||
logger.error('openDrivePicker callback not provided')
|
||||
this.setState(ClientToolCallState.error)
|
||||
await this.markToolComplete(400, 'Missing drive picker context')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
this.setState(ClientToolCallState.executing)
|
||||
|
||||
// Fetch credentials list
|
||||
const credsRes = await fetch(`/api/auth/oauth/credentials?provider=google-drive`)
|
||||
if (!credsRes.ok) {
|
||||
throw new Error(`Failed to load OAuth credentials (${credsRes.status})`)
|
||||
}
|
||||
const credsData = await credsRes.json()
|
||||
const creds = Array.isArray(credsData.credentials) ? credsData.credentials : []
|
||||
if (creds.length === 0) {
|
||||
throw new Error('No OAuth credentials found')
|
||||
}
|
||||
const defaultCred = creds.find((c: any) => c.isDefault) || creds[0]
|
||||
|
||||
// Exchange for access token
|
||||
const tokenRes = await fetch('/api/auth/oauth/token', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ credentialId: defaultCred.id }),
|
||||
})
|
||||
if (!tokenRes.ok) {
|
||||
throw new Error(`Failed to fetch access token (${tokenRes.status})`)
|
||||
}
|
||||
const { accessToken } = await tokenRes.json()
|
||||
if (!accessToken) {
|
||||
throw new Error('Missing access token in response')
|
||||
}
|
||||
|
||||
// Open picker using provided UI callback
|
||||
const picked = await ctx.openDrivePicker(accessToken)
|
||||
if (!picked) {
|
||||
// User canceled
|
||||
await this.markToolComplete(200, 'Tool execution was skipped by the user')
|
||||
this.setState(ClientToolCallState.rejected)
|
||||
return
|
||||
}
|
||||
|
||||
// Mark success
|
||||
await this.markToolComplete(200, { granted: true })
|
||||
this.setState(ClientToolCallState.success)
|
||||
} catch (error: any) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
await this.markToolComplete(500, message)
|
||||
this.setState(ClientToolCallState.error)
|
||||
}
|
||||
}
|
||||
|
||||
async handleReject(): Promise<void> {
|
||||
await super.handleReject()
|
||||
this.setState(ClientToolCallState.rejected)
|
||||
}
|
||||
|
||||
async execute(args?: any): Promise<void> {
|
||||
await this.handleAccept(args)
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import { GitBranch, Loader2, MinusCircle, XCircle } from 'lucide-react'
|
||||
import {
|
||||
BaseClientTool,
|
||||
type BaseClientToolMetadata,
|
||||
ClientToolCallState,
|
||||
} from '@/lib/copilot/tools/client/base-tool'
|
||||
|
||||
interface GenerateDiagramArgs {
|
||||
diagramText: string
|
||||
language?: 'mermaid'
|
||||
}
|
||||
|
||||
/**
|
||||
* Client tool for rendering diagrams in the copilot chat.
|
||||
* This tool renders mermaid diagrams directly in the UI without server execution.
|
||||
*/
|
||||
export class GenerateDiagramClientTool extends BaseClientTool {
|
||||
static readonly id = 'generate_diagram'
|
||||
|
||||
constructor(toolCallId: string) {
|
||||
super(toolCallId, GenerateDiagramClientTool.id, GenerateDiagramClientTool.metadata)
|
||||
}
|
||||
|
||||
static readonly metadata: BaseClientToolMetadata = {
|
||||
displayNames: {
|
||||
[ClientToolCallState.generating]: { text: 'Designing workflow', icon: Loader2 },
|
||||
[ClientToolCallState.pending]: { text: 'Designing workflow', icon: Loader2 },
|
||||
[ClientToolCallState.executing]: { text: 'Designing workflow', icon: Loader2 },
|
||||
[ClientToolCallState.success]: { text: 'Designed workflow', icon: GitBranch },
|
||||
[ClientToolCallState.error]: { text: 'Failed to design workflow', icon: XCircle },
|
||||
[ClientToolCallState.aborted]: { text: 'Aborted designing workflow', icon: MinusCircle },
|
||||
[ClientToolCallState.rejected]: { text: 'Skipped designing workflow', icon: MinusCircle },
|
||||
},
|
||||
interrupt: undefined,
|
||||
}
|
||||
|
||||
async execute(args?: GenerateDiagramArgs): Promise<void> {
|
||||
try {
|
||||
this.setState(ClientToolCallState.executing)
|
||||
|
||||
const diagramText = args?.diagramText
|
||||
const language = args?.language || 'mermaid'
|
||||
|
||||
if (!diagramText?.trim()) {
|
||||
await this.markToolComplete(400, 'No diagram text provided')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
// The actual rendering happens in the UI component (tool-call.tsx)
|
||||
// We just need to mark the tool as complete with the diagram data
|
||||
await this.markToolComplete(200, 'Diagram rendered successfully', {
|
||||
diagramText,
|
||||
language,
|
||||
rendered: true,
|
||||
})
|
||||
this.setState(ClientToolCallState.success)
|
||||
} catch (error: any) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
await this.markToolComplete(500, message)
|
||||
this.setState(ClientToolCallState.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,9 +147,44 @@ export class EditWorkflowClientTool extends BaseClientTool {
|
||||
// Get the workflow state that was applied, merge subblocks, and sanitize
|
||||
// This matches what get_user_workflow would return
|
||||
const workflowJson = this.getSanitizedWorkflowJson(currentState)
|
||||
const sanitizedData = workflowJson ? { userWorkflow: workflowJson } : undefined
|
||||
|
||||
await this.markToolComplete(200, 'Workflow edits accepted', sanitizedData)
|
||||
// Build sanitized data including workflow JSON and any skipped/validation info from the result
|
||||
const sanitizedData: Record<string, any> = {}
|
||||
if (workflowJson) {
|
||||
sanitizedData.userWorkflow = workflowJson
|
||||
}
|
||||
|
||||
// Include skipped items and validation errors in the accept response for LLM feedback
|
||||
if (this.lastResult?.skippedItems?.length > 0) {
|
||||
sanitizedData.skippedItems = this.lastResult.skippedItems
|
||||
sanitizedData.skippedItemsMessage = this.lastResult.skippedItemsMessage
|
||||
}
|
||||
if (this.lastResult?.inputValidationErrors?.length > 0) {
|
||||
sanitizedData.inputValidationErrors = this.lastResult.inputValidationErrors
|
||||
sanitizedData.inputValidationMessage = this.lastResult.inputValidationMessage
|
||||
}
|
||||
|
||||
// Build a message that includes info about skipped items
|
||||
let acceptMessage = 'Workflow edits accepted'
|
||||
if (
|
||||
this.lastResult?.skippedItems?.length > 0 ||
|
||||
this.lastResult?.inputValidationErrors?.length > 0
|
||||
) {
|
||||
const parts: string[] = []
|
||||
if (this.lastResult?.skippedItems?.length > 0) {
|
||||
parts.push(`${this.lastResult.skippedItems.length} operation(s) were skipped`)
|
||||
}
|
||||
if (this.lastResult?.inputValidationErrors?.length > 0) {
|
||||
parts.push(`${this.lastResult.inputValidationErrors.length} input(s) were rejected`)
|
||||
}
|
||||
acceptMessage = `Workflow edits accepted. Note: ${parts.join(', ')}.`
|
||||
}
|
||||
|
||||
await this.markToolComplete(
|
||||
200,
|
||||
acceptMessage,
|
||||
Object.keys(sanitizedData).length > 0 ? sanitizedData : undefined
|
||||
)
|
||||
this.setState(ClientToolCallState.success)
|
||||
}
|
||||
|
||||
@@ -248,8 +283,24 @@ export class EditWorkflowClientTool extends BaseClientTool {
|
||||
blocksCount: result?.workflowState
|
||||
? Object.keys(result.workflowState.blocks || {}).length
|
||||
: 0,
|
||||
hasSkippedItems: !!result?.skippedItems,
|
||||
skippedItemsCount: result?.skippedItems?.length || 0,
|
||||
hasInputValidationErrors: !!result?.inputValidationErrors,
|
||||
inputValidationErrorsCount: result?.inputValidationErrors?.length || 0,
|
||||
})
|
||||
|
||||
// Log skipped items and validation errors for visibility
|
||||
if (result?.skippedItems?.length > 0) {
|
||||
logger.warn('Some operations were skipped during edit_workflow', {
|
||||
skippedItems: result.skippedItems,
|
||||
})
|
||||
}
|
||||
if (result?.inputValidationErrors?.length > 0) {
|
||||
logger.warn('Some inputs were rejected during edit_workflow', {
|
||||
inputValidationErrors: result.inputValidationErrors,
|
||||
})
|
||||
}
|
||||
|
||||
// Update diff directly with workflow state - no YAML conversion needed!
|
||||
// The diff engine may transform the workflow state (e.g., assign new IDs), so we must use
|
||||
// the returned proposedState rather than the original result.workflowState
|
||||
@@ -288,10 +339,42 @@ export class EditWorkflowClientTool extends BaseClientTool {
|
||||
// Get the workflow state that was just applied, merge subblocks, and sanitize
|
||||
// This matches what get_user_workflow would return (the true state after edits were applied)
|
||||
const workflowJson = this.getSanitizedWorkflowJson(actualDiffWorkflow)
|
||||
const sanitizedData = workflowJson ? { userWorkflow: workflowJson } : undefined
|
||||
|
||||
// Build sanitized data including workflow JSON and any skipped/validation info
|
||||
const sanitizedData: Record<string, any> = {}
|
||||
if (workflowJson) {
|
||||
sanitizedData.userWorkflow = workflowJson
|
||||
}
|
||||
|
||||
// Include skipped items and validation errors in the response for LLM feedback
|
||||
if (result?.skippedItems?.length > 0) {
|
||||
sanitizedData.skippedItems = result.skippedItems
|
||||
sanitizedData.skippedItemsMessage = result.skippedItemsMessage
|
||||
}
|
||||
if (result?.inputValidationErrors?.length > 0) {
|
||||
sanitizedData.inputValidationErrors = result.inputValidationErrors
|
||||
sanitizedData.inputValidationMessage = result.inputValidationMessage
|
||||
}
|
||||
|
||||
// Build a message that includes info about skipped items
|
||||
let completeMessage = 'Workflow diff ready for review'
|
||||
if (result?.skippedItems?.length > 0 || result?.inputValidationErrors?.length > 0) {
|
||||
const parts: string[] = []
|
||||
if (result?.skippedItems?.length > 0) {
|
||||
parts.push(`${result.skippedItems.length} operation(s) skipped`)
|
||||
}
|
||||
if (result?.inputValidationErrors?.length > 0) {
|
||||
parts.push(`${result.inputValidationErrors.length} input(s) rejected`)
|
||||
}
|
||||
completeMessage = `Workflow diff ready for review. Note: ${parts.join(', ')}.`
|
||||
}
|
||||
|
||||
// Mark complete early to unblock LLM stream
|
||||
await this.markToolComplete(200, 'Workflow diff ready for review', sanitizedData)
|
||||
await this.markToolComplete(
|
||||
200,
|
||||
completeMessage,
|
||||
Object.keys(sanitizedData).length > 0 ? sanitizedData : undefined
|
||||
)
|
||||
|
||||
// Move into review state
|
||||
this.setState(ClientToolCallState.review, { result })
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { List, Loader2, X, XCircle } from 'lucide-react'
|
||||
import {
|
||||
BaseClientTool,
|
||||
type BaseClientToolMetadata,
|
||||
ClientToolCallState,
|
||||
} from '@/lib/copilot/tools/client/base-tool'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
const logger = createLogger('GetGlobalWorkflowVariablesClientTool')
|
||||
|
||||
export class GetGlobalWorkflowVariablesClientTool extends BaseClientTool {
|
||||
static readonly id = 'get_global_workflow_variables'
|
||||
|
||||
constructor(toolCallId: string) {
|
||||
super(
|
||||
toolCallId,
|
||||
GetGlobalWorkflowVariablesClientTool.id,
|
||||
GetGlobalWorkflowVariablesClientTool.metadata
|
||||
)
|
||||
}
|
||||
|
||||
static readonly metadata: BaseClientToolMetadata = {
|
||||
displayNames: {
|
||||
[ClientToolCallState.generating]: { text: 'Fetching workflow variables', icon: Loader2 },
|
||||
[ClientToolCallState.pending]: { text: 'Fetching workflow variables', icon: List },
|
||||
[ClientToolCallState.executing]: { text: 'Fetching workflow variables', icon: Loader2 },
|
||||
[ClientToolCallState.aborted]: { text: 'Aborted fetching variables', icon: XCircle },
|
||||
[ClientToolCallState.success]: { text: 'Workflow variables retrieved', icon: List },
|
||||
[ClientToolCallState.error]: { text: 'Failed to fetch variables', icon: X },
|
||||
[ClientToolCallState.rejected]: { text: 'Skipped fetching variables', icon: XCircle },
|
||||
},
|
||||
}
|
||||
|
||||
async execute(): Promise<void> {
|
||||
try {
|
||||
this.setState(ClientToolCallState.executing)
|
||||
const { activeWorkflowId } = useWorkflowRegistry.getState()
|
||||
if (!activeWorkflowId) {
|
||||
await this.markToolComplete(400, 'No active workflow found')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/workflows/${activeWorkflowId}/variables`, { method: 'GET' })
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => '')
|
||||
await this.markToolComplete(res.status, text || 'Failed to fetch workflow variables')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
const json = await res.json()
|
||||
const varsRecord = (json?.data as Record<string, any>) || {}
|
||||
// Convert to id/name/value for clarity
|
||||
const variables = Object.values(varsRecord).map((v: any) => ({
|
||||
id: String(v?.id || ''),
|
||||
name: String(v?.name || ''),
|
||||
value: (v as any)?.value,
|
||||
}))
|
||||
logger.info('Fetched workflow variables', { count: variables.length })
|
||||
await this.markToolComplete(200, `Found ${variables.length} variable(s)`, { variables })
|
||||
this.setState(ClientToolCallState.success)
|
||||
} catch (error: any) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
await this.markToolComplete(500, message || 'Failed to fetch workflow variables')
|
||||
this.setState(ClientToolCallState.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
269
apps/sim/lib/copilot/tools/client/workflow/get-workflow-data.ts
Normal file
269
apps/sim/lib/copilot/tools/client/workflow/get-workflow-data.ts
Normal file
@@ -0,0 +1,269 @@
|
||||
import { Database, Loader2, X, XCircle } from 'lucide-react'
|
||||
import {
|
||||
BaseClientTool,
|
||||
type BaseClientToolMetadata,
|
||||
ClientToolCallState,
|
||||
} from '@/lib/copilot/tools/client/base-tool'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
const logger = createLogger('GetWorkflowDataClientTool')
|
||||
|
||||
/** Data type enum for the get_workflow_data tool */
|
||||
export type WorkflowDataType = 'global_variables' | 'custom_tools' | 'mcp_tools' | 'files'
|
||||
|
||||
interface GetWorkflowDataArgs {
|
||||
data_type: WorkflowDataType
|
||||
}
|
||||
|
||||
export class GetWorkflowDataClientTool extends BaseClientTool {
|
||||
static readonly id = 'get_workflow_data'
|
||||
|
||||
constructor(toolCallId: string) {
|
||||
super(toolCallId, GetWorkflowDataClientTool.id, GetWorkflowDataClientTool.metadata)
|
||||
}
|
||||
|
||||
static readonly metadata: BaseClientToolMetadata = {
|
||||
displayNames: {
|
||||
[ClientToolCallState.generating]: { text: 'Fetching workflow data', icon: Loader2 },
|
||||
[ClientToolCallState.pending]: { text: 'Fetching workflow data', icon: Database },
|
||||
[ClientToolCallState.executing]: { text: 'Fetching workflow data', icon: Loader2 },
|
||||
[ClientToolCallState.aborted]: { text: 'Aborted fetching data', icon: XCircle },
|
||||
[ClientToolCallState.success]: { text: 'Workflow data retrieved', icon: Database },
|
||||
[ClientToolCallState.error]: { text: 'Failed to fetch data', icon: X },
|
||||
[ClientToolCallState.rejected]: { text: 'Skipped fetching data', icon: XCircle },
|
||||
},
|
||||
getDynamicText: (params, state) => {
|
||||
const dataType = params?.data_type as WorkflowDataType | undefined
|
||||
if (!dataType) return undefined
|
||||
|
||||
const typeLabels: Record<WorkflowDataType, string> = {
|
||||
global_variables: 'variables',
|
||||
custom_tools: 'custom tools',
|
||||
mcp_tools: 'MCP tools',
|
||||
files: 'files',
|
||||
}
|
||||
|
||||
const label = typeLabels[dataType] || dataType
|
||||
|
||||
switch (state) {
|
||||
case ClientToolCallState.success:
|
||||
return `Retrieved ${label}`
|
||||
case ClientToolCallState.executing:
|
||||
case ClientToolCallState.generating:
|
||||
return `Fetching ${label}`
|
||||
case ClientToolCallState.pending:
|
||||
return `Fetch ${label}?`
|
||||
case ClientToolCallState.error:
|
||||
return `Failed to fetch ${label}`
|
||||
case ClientToolCallState.aborted:
|
||||
return `Aborted fetching ${label}`
|
||||
case ClientToolCallState.rejected:
|
||||
return `Skipped fetching ${label}`
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
}
|
||||
|
||||
async execute(args?: GetWorkflowDataArgs): Promise<void> {
|
||||
try {
|
||||
this.setState(ClientToolCallState.executing)
|
||||
|
||||
const dataType = args?.data_type
|
||||
if (!dataType) {
|
||||
await this.markToolComplete(400, 'Missing data_type parameter')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
const { activeWorkflowId, hydration } = useWorkflowRegistry.getState()
|
||||
const activeWorkspaceId = hydration.workspaceId
|
||||
|
||||
switch (dataType) {
|
||||
case 'global_variables':
|
||||
await this.fetchGlobalVariables(activeWorkflowId)
|
||||
break
|
||||
case 'custom_tools':
|
||||
await this.fetchCustomTools(activeWorkspaceId)
|
||||
break
|
||||
case 'mcp_tools':
|
||||
await this.fetchMcpTools(activeWorkspaceId)
|
||||
break
|
||||
case 'files':
|
||||
await this.fetchFiles(activeWorkspaceId)
|
||||
break
|
||||
default:
|
||||
await this.markToolComplete(400, `Unknown data_type: ${dataType}`)
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
await this.markToolComplete(500, message || 'Failed to fetch workflow data')
|
||||
this.setState(ClientToolCallState.error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch global workflow variables
|
||||
*/
|
||||
private async fetchGlobalVariables(workflowId: string | null): Promise<void> {
|
||||
if (!workflowId) {
|
||||
await this.markToolComplete(400, 'No active workflow found')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/workflows/${workflowId}/variables`, { method: 'GET' })
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => '')
|
||||
await this.markToolComplete(res.status, text || 'Failed to fetch workflow variables')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
const json = await res.json()
|
||||
const varsRecord = (json?.data as Record<string, unknown>) || {}
|
||||
const variables = Object.values(varsRecord).map((v: unknown) => {
|
||||
const variable = v as { id?: string; name?: string; value?: unknown }
|
||||
return {
|
||||
id: String(variable?.id || ''),
|
||||
name: String(variable?.name || ''),
|
||||
value: variable?.value,
|
||||
}
|
||||
})
|
||||
|
||||
logger.info('Fetched workflow variables', { count: variables.length })
|
||||
await this.markToolComplete(200, `Found ${variables.length} variable(s)`, { variables })
|
||||
this.setState(ClientToolCallState.success)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch custom tools for the workspace
|
||||
*/
|
||||
private async fetchCustomTools(workspaceId: string | null): Promise<void> {
|
||||
if (!workspaceId) {
|
||||
await this.markToolComplete(400, 'No active workspace found')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/tools/custom?workspaceId=${workspaceId}`, { method: 'GET' })
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => '')
|
||||
await this.markToolComplete(res.status, text || 'Failed to fetch custom tools')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
const json = await res.json()
|
||||
const toolsData = (json?.data as unknown[]) || []
|
||||
const customTools = toolsData.map((tool: unknown) => {
|
||||
const t = tool as {
|
||||
id?: string
|
||||
title?: string
|
||||
schema?: { function?: { name?: string; description?: string; parameters?: unknown } }
|
||||
code?: string
|
||||
}
|
||||
return {
|
||||
id: String(t?.id || ''),
|
||||
title: String(t?.title || ''),
|
||||
functionName: String(t?.schema?.function?.name || ''),
|
||||
description: String(t?.schema?.function?.description || ''),
|
||||
parameters: t?.schema?.function?.parameters,
|
||||
}
|
||||
})
|
||||
|
||||
logger.info('Fetched custom tools', { count: customTools.length })
|
||||
await this.markToolComplete(200, `Found ${customTools.length} custom tool(s)`, { customTools })
|
||||
this.setState(ClientToolCallState.success)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch MCP tools for the workspace
|
||||
*/
|
||||
private async fetchMcpTools(workspaceId: string | null): Promise<void> {
|
||||
if (!workspaceId) {
|
||||
await this.markToolComplete(400, 'No active workspace found')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/mcp/tools/discover?workspaceId=${workspaceId}`, { method: 'GET' })
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => '')
|
||||
await this.markToolComplete(res.status, text || 'Failed to fetch MCP tools')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
const json = await res.json()
|
||||
const toolsData = (json?.data?.tools as unknown[]) || []
|
||||
const mcpTools = toolsData.map((tool: unknown) => {
|
||||
const t = tool as {
|
||||
name?: string
|
||||
serverId?: string
|
||||
serverName?: string
|
||||
description?: string
|
||||
inputSchema?: unknown
|
||||
}
|
||||
return {
|
||||
name: String(t?.name || ''),
|
||||
serverId: String(t?.serverId || ''),
|
||||
serverName: String(t?.serverName || ''),
|
||||
description: String(t?.description || ''),
|
||||
inputSchema: t?.inputSchema,
|
||||
}
|
||||
})
|
||||
|
||||
logger.info('Fetched MCP tools', { count: mcpTools.length })
|
||||
await this.markToolComplete(200, `Found ${mcpTools.length} MCP tool(s)`, { mcpTools })
|
||||
this.setState(ClientToolCallState.success)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch workspace files metadata
|
||||
*/
|
||||
private async fetchFiles(workspaceId: string | null): Promise<void> {
|
||||
if (!workspaceId) {
|
||||
await this.markToolComplete(400, 'No active workspace found')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/workspaces/${workspaceId}/files`, { method: 'GET' })
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => '')
|
||||
await this.markToolComplete(res.status, text || 'Failed to fetch files')
|
||||
this.setState(ClientToolCallState.error)
|
||||
return
|
||||
}
|
||||
|
||||
const json = await res.json()
|
||||
const filesData = (json?.files as unknown[]) || []
|
||||
const files = filesData.map((file: unknown) => {
|
||||
const f = file as {
|
||||
id?: string
|
||||
name?: string
|
||||
key?: string
|
||||
path?: string
|
||||
size?: number
|
||||
type?: string
|
||||
uploadedAt?: string
|
||||
}
|
||||
return {
|
||||
id: String(f?.id || ''),
|
||||
name: String(f?.name || ''),
|
||||
key: String(f?.key || ''),
|
||||
path: String(f?.path || ''),
|
||||
size: Number(f?.size || 0),
|
||||
type: String(f?.type || ''),
|
||||
uploadedAt: String(f?.uploadedAt || ''),
|
||||
}
|
||||
})
|
||||
|
||||
logger.info('Fetched workspace files', { count: files.length })
|
||||
await this.markToolComplete(200, `Found ${files.length} file(s)`, { files })
|
||||
this.setState(ClientToolCallState.success)
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import type { BaseServerTool } from '@/lib/copilot/tools/server/base-tool'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getOAuthToken, getUserId } from '@/app/api/auth/oauth/utils'
|
||||
import { executeTool } from '@/tools'
|
||||
|
||||
interface ListGDriveFilesParams {
|
||||
userId?: string
|
||||
workflowId?: string
|
||||
search_query?: string
|
||||
searchQuery?: string
|
||||
num_results?: number
|
||||
}
|
||||
|
||||
export const listGDriveFilesServerTool: BaseServerTool<ListGDriveFilesParams, any> = {
|
||||
name: 'list_gdrive_files',
|
||||
async execute(params: ListGDriveFilesParams): Promise<any> {
|
||||
const logger = createLogger('ListGDriveFilesServerTool')
|
||||
const { search_query, searchQuery, num_results } = params || {}
|
||||
let uid = params?.userId
|
||||
if (!uid && params?.workflowId) {
|
||||
uid = await getUserId('copilot-gdrive-list', params.workflowId)
|
||||
}
|
||||
if (!uid || typeof uid !== 'string' || uid.trim().length === 0) {
|
||||
throw new Error('userId is required')
|
||||
}
|
||||
|
||||
const query = search_query ?? searchQuery
|
||||
const pageSize = num_results
|
||||
|
||||
const accessToken = await getOAuthToken(uid, 'google-drive')
|
||||
if (!accessToken) {
|
||||
throw new Error(
|
||||
'No Google Drive connection found for this user. Please connect Google Drive in settings.'
|
||||
)
|
||||
}
|
||||
|
||||
const result = await executeTool(
|
||||
'google_drive_list',
|
||||
{
|
||||
accessToken,
|
||||
...(query ? { query } : {}),
|
||||
...(typeof pageSize === 'number' ? { pageSize } : {}),
|
||||
},
|
||||
true
|
||||
)
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to list Google Drive files')
|
||||
}
|
||||
const output = (result as any).output || result
|
||||
const files = Array.isArray(output?.files) ? output.files : output?.output?.files || []
|
||||
const nextPageToken = output?.nextPageToken || output?.output?.nextPageToken
|
||||
logger.info('Listed Google Drive files', { count: files.length })
|
||||
return { files, total: files.length, nextPageToken }
|
||||
},
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import type { BaseServerTool } from '@/lib/copilot/tools/server/base-tool'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getOAuthToken, getUserId } from '@/app/api/auth/oauth/utils'
|
||||
import { executeTool } from '@/tools'
|
||||
|
||||
interface ReadGDriveFileParams {
|
||||
userId?: string
|
||||
workflowId?: string
|
||||
fileId?: string
|
||||
file_id?: string
|
||||
// Accept common alternate identifiers
|
||||
id?: string
|
||||
spreadsheetId?: string
|
||||
documentId?: string
|
||||
url?: string
|
||||
link?: string
|
||||
webViewLink?: string
|
||||
type?: 'doc' | 'sheet' | 'document' | 'spreadsheet'
|
||||
range?: string
|
||||
}
|
||||
|
||||
function extractIdAndTypeFromUrl(urlString?: string): { fileId?: string; type?: 'doc' | 'sheet' } {
|
||||
if (!urlString) return {}
|
||||
try {
|
||||
const url = new URL(urlString)
|
||||
const host = url.hostname
|
||||
const pathname = url.pathname
|
||||
|
||||
// docs.google.com/document/d/{id}/...
|
||||
if (host.includes('docs.google.com') && pathname.includes('/document/')) {
|
||||
const parts = pathname.split('/').filter(Boolean)
|
||||
const dIndex = parts.indexOf('d')
|
||||
const id = dIndex >= 0 && parts[dIndex + 1] ? parts[dIndex + 1] : undefined
|
||||
return { fileId: id, type: 'doc' }
|
||||
}
|
||||
|
||||
// docs.google.com/spreadsheets/d/{id}/...
|
||||
if (host.includes('docs.google.com') && pathname.includes('/spreadsheets/')) {
|
||||
const parts = pathname.split('/').filter(Boolean)
|
||||
const dIndex = parts.indexOf('d')
|
||||
const id = dIndex >= 0 && parts[dIndex + 1] ? parts[dIndex + 1] : undefined
|
||||
return { fileId: id, type: 'sheet' }
|
||||
}
|
||||
|
||||
// drive.google.com/file/d/{id}/view
|
||||
if (host.includes('drive.google.com') && pathname.includes('/file/')) {
|
||||
const parts = pathname.split('/').filter(Boolean)
|
||||
const dIndex = parts.indexOf('d')
|
||||
const id = dIndex >= 0 && parts[dIndex + 1] ? parts[dIndex + 1] : undefined
|
||||
return { fileId: id, type: 'doc' }
|
||||
}
|
||||
} catch {}
|
||||
return {}
|
||||
}
|
||||
|
||||
export const readGDriveFileServerTool: BaseServerTool<ReadGDriveFileParams, any> = {
|
||||
name: 'read_gdrive_file',
|
||||
async execute(params: ReadGDriveFileParams): Promise<any> {
|
||||
const logger = createLogger('ReadGDriveFileServerTool')
|
||||
|
||||
// Normalize inputs
|
||||
let userId = params?.userId
|
||||
// Always try to resolve from workflow or session when not provided
|
||||
if (!userId) {
|
||||
userId = await getUserId('copilot-gdrive-read', params?.workflowId)
|
||||
}
|
||||
|
||||
// Normalize fileId from multiple possible fields
|
||||
let fileId =
|
||||
params?.fileId || params?.file_id || params?.id || params?.spreadsheetId || params?.documentId
|
||||
|
||||
// If a URL/link is passed, extract id and possibly type
|
||||
if (!fileId && (params?.url || params?.link || params?.webViewLink)) {
|
||||
const { fileId: extractedId, type: extractedType } = extractIdAndTypeFromUrl(
|
||||
params.url || params.link || params.webViewLink
|
||||
)
|
||||
fileId = extractedId || fileId
|
||||
if (!params?.type && extractedType) params.type = extractedType
|
||||
}
|
||||
|
||||
let type = params?.type as string | undefined
|
||||
if (type === 'document' || type === 'docs') type = 'doc'
|
||||
if (type === 'spreadsheet' || type === 'sheets') type = 'sheet'
|
||||
|
||||
// Infer type from provided identifiers if still missing
|
||||
if (!type) {
|
||||
if (params?.spreadsheetId) type = 'sheet'
|
||||
else if (params?.documentId) type = 'doc'
|
||||
}
|
||||
|
||||
logger.info('read_gdrive_file input', {
|
||||
hasUserId: !!userId,
|
||||
hasWorkflowId: !!params?.workflowId,
|
||||
hasFileId: !!fileId,
|
||||
type,
|
||||
hasRange: !!params?.range,
|
||||
})
|
||||
|
||||
if (!userId || !fileId || !type) throw new Error('userId, fileId and type are required')
|
||||
|
||||
if (type === 'doc') {
|
||||
const accessToken = await getOAuthToken(userId, 'google-drive')
|
||||
if (!accessToken)
|
||||
throw new Error(
|
||||
'No Google Drive connection found for this user. Please connect Google Drive in settings.'
|
||||
)
|
||||
const result = await executeTool('google_drive_get_content', { accessToken, fileId }, true)
|
||||
if (!result.success) throw new Error(result.error || 'Failed to read Google Drive document')
|
||||
const output = (result as any).output || result
|
||||
const content = output?.output?.content ?? output?.content
|
||||
const metadata = output?.output?.metadata ?? output?.metadata
|
||||
return { type, content, metadata }
|
||||
}
|
||||
|
||||
if (type === 'sheet') {
|
||||
const accessToken = await getOAuthToken(userId, 'google-sheets')
|
||||
if (!accessToken)
|
||||
throw new Error(
|
||||
'No Google Sheets connection found for this user. Please connect Google Sheets in settings.'
|
||||
)
|
||||
const result = await executeTool(
|
||||
'google_sheets_read',
|
||||
{ accessToken, spreadsheetId: fileId, ...(params?.range ? { range: params.range } : {}) },
|
||||
true
|
||||
)
|
||||
if (!result.success) throw new Error(result.error || 'Failed to read Google Sheets data')
|
||||
const output = (result as any).output || result
|
||||
const rows: string[][] = output?.output?.data?.values || output?.data?.values || []
|
||||
const resolvedRange: string | undefined = output?.output?.data?.range || output?.data?.range
|
||||
const metadata = output?.output?.metadata || output?.metadata
|
||||
return { type, rows, range: resolvedRange, metadata }
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported type: ${type}`)
|
||||
},
|
||||
}
|
||||
@@ -3,8 +3,6 @@ import { getBlocksAndToolsServerTool } from '@/lib/copilot/tools/server/blocks/g
|
||||
import { getBlocksMetadataServerTool } from '@/lib/copilot/tools/server/blocks/get-blocks-metadata-tool'
|
||||
import { getTriggerBlocksServerTool } from '@/lib/copilot/tools/server/blocks/get-trigger-blocks'
|
||||
import { searchDocumentationServerTool } from '@/lib/copilot/tools/server/docs/search-documentation'
|
||||
import { listGDriveFilesServerTool } from '@/lib/copilot/tools/server/gdrive/list-files'
|
||||
import { readGDriveFileServerTool } from '@/lib/copilot/tools/server/gdrive/read-file'
|
||||
import {
|
||||
KnowledgeBaseInput,
|
||||
knowledgeBaseServerTool,
|
||||
@@ -43,8 +41,6 @@ serverToolRegistry[getWorkflowConsoleServerTool.name] = getWorkflowConsoleServer
|
||||
serverToolRegistry[searchDocumentationServerTool.name] = searchDocumentationServerTool
|
||||
serverToolRegistry[searchOnlineServerTool.name] = searchOnlineServerTool
|
||||
serverToolRegistry[setEnvironmentVariablesServerTool.name] = setEnvironmentVariablesServerTool
|
||||
serverToolRegistry[listGDriveFilesServerTool.name] = listGDriveFilesServerTool
|
||||
serverToolRegistry[readGDriveFileServerTool.name] = readGDriveFileServerTool
|
||||
serverToolRegistry[getCredentialsServerTool.name] = getCredentialsServerTool
|
||||
serverToolRegistry[makeApiRequestServerTool.name] = makeApiRequestServerTool
|
||||
serverToolRegistry[knowledgeBaseServerTool.name] = knowledgeBaseServerTool
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
171
apps/sim/lib/copilot/validation/selector-validator.ts
Normal file
171
apps/sim/lib/copilot/validation/selector-validator.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
import { db } from '@sim/db'
|
||||
import { account, document, knowledgeBase, mcpServers, workflow } from '@sim/db/schema'
|
||||
import { and, eq, inArray, isNull } from 'drizzle-orm'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('SelectorValidator')
|
||||
|
||||
/**
|
||||
* Result of selector ID validation
|
||||
*/
|
||||
export interface SelectorValidationResult {
|
||||
valid: string[]
|
||||
invalid: string[]
|
||||
warning?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that selector IDs exist in the database
|
||||
* Returns valid IDs, invalid IDs, and optional warning for unknown selector types
|
||||
*/
|
||||
export async function validateSelectorIds(
|
||||
selectorType: string,
|
||||
ids: string | string[],
|
||||
context: { userId: string; workspaceId?: string }
|
||||
): Promise<SelectorValidationResult> {
|
||||
const idsArray = (Array.isArray(ids) ? ids : [ids]).filter((id) => id && id.trim() !== '')
|
||||
if (idsArray.length === 0) {
|
||||
return { valid: [], invalid: [] }
|
||||
}
|
||||
|
||||
let existingIds: string[] = []
|
||||
|
||||
try {
|
||||
switch (selectorType) {
|
||||
case 'oauth-input': {
|
||||
// Credentials must belong to the user
|
||||
const results = await db
|
||||
.select({ id: account.id })
|
||||
.from(account)
|
||||
.where(and(inArray(account.id, idsArray), eq(account.userId, context.userId)))
|
||||
existingIds = results.map((r) => r.id)
|
||||
break
|
||||
}
|
||||
|
||||
case 'knowledge-base-selector': {
|
||||
// Knowledge bases - check if they exist (workspace check optional)
|
||||
const conditions = [inArray(knowledgeBase.id, idsArray)]
|
||||
if (context.workspaceId) {
|
||||
conditions.push(eq(knowledgeBase.workspaceId, context.workspaceId))
|
||||
}
|
||||
const results = await db
|
||||
.select({ id: knowledgeBase.id })
|
||||
.from(knowledgeBase)
|
||||
.where(and(...conditions))
|
||||
existingIds = results.map((r) => r.id)
|
||||
break
|
||||
}
|
||||
|
||||
case 'workflow-selector': {
|
||||
// Workflows - check if they exist
|
||||
const results = await db
|
||||
.select({ id: workflow.id })
|
||||
.from(workflow)
|
||||
.where(inArray(workflow.id, idsArray))
|
||||
existingIds = results.map((r) => r.id)
|
||||
break
|
||||
}
|
||||
|
||||
case 'document-selector': {
|
||||
// Documents in knowledge bases - check if they exist and are not deleted
|
||||
const results = await db
|
||||
.select({ id: document.id })
|
||||
.from(document)
|
||||
.where(and(inArray(document.id, idsArray), isNull(document.deletedAt)))
|
||||
existingIds = results.map((r) => r.id)
|
||||
break
|
||||
}
|
||||
|
||||
case 'mcp-server-selector': {
|
||||
// MCP servers - check if they exist, are enabled, and not deleted in the workspace
|
||||
const conditions = [
|
||||
inArray(mcpServers.id, idsArray),
|
||||
eq(mcpServers.enabled, true),
|
||||
isNull(mcpServers.deletedAt),
|
||||
]
|
||||
if (context.workspaceId) {
|
||||
conditions.push(eq(mcpServers.workspaceId, context.workspaceId))
|
||||
}
|
||||
const results = await db
|
||||
.select({ id: mcpServers.id })
|
||||
.from(mcpServers)
|
||||
.where(and(...conditions))
|
||||
existingIds = results.map((r) => r.id)
|
||||
break
|
||||
}
|
||||
|
||||
// MCP tools are dynamically fetched from external MCP servers at runtime
|
||||
// We can't validate tool IDs locally - only the server connection validates them
|
||||
case 'mcp-tool-selector': {
|
||||
return { valid: idsArray, invalid: [] }
|
||||
}
|
||||
|
||||
// These selectors use external IDs from third-party systems (Slack, Google, Jira, etc.)
|
||||
// We can't validate them locally - they're validated at runtime when calling the external API
|
||||
case 'file-selector':
|
||||
case 'project-selector':
|
||||
case 'channel-selector':
|
||||
case 'folder-selector': {
|
||||
// External/dynamic selectors - skip validation, IDs are validated at runtime
|
||||
// These IDs come from: Slack channels, Google Drive files, Jira projects, etc.
|
||||
return { valid: idsArray, invalid: [] }
|
||||
}
|
||||
|
||||
default: {
|
||||
// Unknown selector type - skip validation but warn
|
||||
logger.warn(`Unknown selector type "${selectorType}" - ID validation skipped`, {
|
||||
selectorType,
|
||||
idCount: idsArray.length,
|
||||
})
|
||||
return {
|
||||
valid: idsArray,
|
||||
invalid: [],
|
||||
warning: `Unknown selector type "${selectorType}" - ID validation skipped`,
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// On DB error, skip validation rather than failing the edit
|
||||
logger.error(`Failed to validate selector IDs for type "${selectorType}"`, {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
selectorType,
|
||||
idCount: idsArray.length,
|
||||
})
|
||||
return {
|
||||
valid: idsArray,
|
||||
invalid: [],
|
||||
warning: `Failed to validate ${selectorType} IDs - validation skipped`,
|
||||
}
|
||||
}
|
||||
|
||||
const existingSet = new Set(existingIds)
|
||||
return {
|
||||
valid: idsArray.filter((id) => existingSet.has(id)),
|
||||
invalid: idsArray.filter((id) => !existingSet.has(id)),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch validate multiple selector fields
|
||||
* Returns a map of field name to validation result
|
||||
*/
|
||||
export async function validateAllSelectorFields(
|
||||
fields: Array<{ fieldName: string; selectorType: string; value: string | string[] }>,
|
||||
context: { userId: string; workspaceId?: string }
|
||||
): Promise<Map<string, SelectorValidationResult>> {
|
||||
const results = new Map<string, SelectorValidationResult>()
|
||||
|
||||
// Run validations in parallel for better performance
|
||||
const validationPromises = fields.map(async ({ fieldName, selectorType, value }) => {
|
||||
const result = await validateSelectorIds(selectorType, value, context)
|
||||
return { fieldName, result }
|
||||
})
|
||||
|
||||
const validationResults = await Promise.all(validationPromises)
|
||||
|
||||
for (const { fieldName, result } of validationResults) {
|
||||
results.set(fieldName, result)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
@@ -193,7 +193,7 @@ export function sanitizeWorkflowForSharing(
|
||||
|
||||
// Clear OAuth credentials (type: 'oauth-input')
|
||||
if (subBlockConfig.type === 'oauth-input') {
|
||||
block.subBlocks[subBlockConfig.id].value = ''
|
||||
block.subBlocks[subBlockConfig.id].value = null
|
||||
}
|
||||
|
||||
// Clear secret fields (password: true)
|
||||
@@ -207,18 +207,18 @@ export function sanitizeWorkflowForSharing(
|
||||
) {
|
||||
// Keep the env var reference
|
||||
} else {
|
||||
block.subBlocks[subBlockConfig.id].value = ''
|
||||
block.subBlocks[subBlockConfig.id].value = null
|
||||
}
|
||||
}
|
||||
|
||||
// Clear workspace-specific selectors
|
||||
else if (WORKSPACE_SPECIFIC_TYPES.has(subBlockConfig.type)) {
|
||||
block.subBlocks[subBlockConfig.id].value = ''
|
||||
block.subBlocks[subBlockConfig.id].value = null
|
||||
}
|
||||
|
||||
// Clear workspace-specific fields by ID
|
||||
else if (WORKSPACE_SPECIFIC_FIELDS.has(subBlockConfig.id)) {
|
||||
block.subBlocks[subBlockConfig.id].value = ''
|
||||
block.subBlocks[subBlockConfig.id].value = null
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -229,7 +229,7 @@ export function sanitizeWorkflowForSharing(
|
||||
Object.entries(block.subBlocks).forEach(([key, subBlock]: [string, any]) => {
|
||||
// Clear workspace-specific fields by key name
|
||||
if (WORKSPACE_SPECIFIC_FIELDS.has(key)) {
|
||||
subBlock.value = ''
|
||||
subBlock.value = null
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -239,11 +239,11 @@ export function sanitizeWorkflowForSharing(
|
||||
Object.entries(block.data).forEach(([key, value]: [string, any]) => {
|
||||
// Clear anything that looks like credentials
|
||||
if (/credential|oauth|api[_-]?key|token|secret|auth|password|bearer/i.test(key)) {
|
||||
block.data[key] = ''
|
||||
block.data[key] = null
|
||||
}
|
||||
// Clear workspace-specific data
|
||||
if (WORKSPACE_SPECIFIC_FIELDS.has(key)) {
|
||||
block.data[key] = ''
|
||||
block.data[key] = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -96,7 +96,6 @@
|
||||
"lodash": "4.17.21",
|
||||
"lucide-react": "^0.479.0",
|
||||
"mammoth": "^1.9.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"mysql2": "3.14.3",
|
||||
"nanoid": "^3.3.7",
|
||||
"next": "16.0.7",
|
||||
|
||||
@@ -15,9 +15,6 @@ import { GetExamplesRagClientTool } from '@/lib/copilot/tools/client/examples/ge
|
||||
import { GetOperationsExamplesClientTool } from '@/lib/copilot/tools/client/examples/get-operations-examples'
|
||||
import { GetTriggerExamplesClientTool } from '@/lib/copilot/tools/client/examples/get-trigger-examples'
|
||||
import { SummarizeClientTool } from '@/lib/copilot/tools/client/examples/summarize'
|
||||
import { ListGDriveFilesClientTool } from '@/lib/copilot/tools/client/gdrive/list-files'
|
||||
import { ReadGDriveFileClientTool } from '@/lib/copilot/tools/client/gdrive/read-file'
|
||||
import { GDriveRequestAccessClientTool } from '@/lib/copilot/tools/client/google/gdrive-request-access'
|
||||
import { KnowledgeBaseClientTool } from '@/lib/copilot/tools/client/knowledge/knowledge-base'
|
||||
import {
|
||||
getClientTool,
|
||||
@@ -26,7 +23,6 @@ import {
|
||||
} from '@/lib/copilot/tools/client/manager'
|
||||
import { NavigateUIClientTool } from '@/lib/copilot/tools/client/navigation/navigate-ui'
|
||||
import { CheckoffTodoClientTool } from '@/lib/copilot/tools/client/other/checkoff-todo'
|
||||
import { GenerateDiagramClientTool } from '@/lib/copilot/tools/client/other/generate-diagram'
|
||||
import { MakeApiRequestClientTool } from '@/lib/copilot/tools/client/other/make-api-request'
|
||||
import { MarkTodoInProgressClientTool } from '@/lib/copilot/tools/client/other/mark-todo-in-progress'
|
||||
import { OAuthRequestAccessClientTool } from '@/lib/copilot/tools/client/other/oauth-request-access'
|
||||
@@ -42,9 +38,9 @@ import { SetEnvironmentVariablesClientTool } from '@/lib/copilot/tools/client/us
|
||||
import { CheckDeploymentStatusClientTool } from '@/lib/copilot/tools/client/workflow/check-deployment-status'
|
||||
import { DeployWorkflowClientTool } from '@/lib/copilot/tools/client/workflow/deploy-workflow'
|
||||
import { EditWorkflowClientTool } from '@/lib/copilot/tools/client/workflow/edit-workflow'
|
||||
import { GetGlobalWorkflowVariablesClientTool } from '@/lib/copilot/tools/client/workflow/get-global-workflow-variables'
|
||||
import { GetUserWorkflowClientTool } from '@/lib/copilot/tools/client/workflow/get-user-workflow'
|
||||
import { GetWorkflowConsoleClientTool } from '@/lib/copilot/tools/client/workflow/get-workflow-console'
|
||||
import { GetWorkflowDataClientTool } from '@/lib/copilot/tools/client/workflow/get-workflow-data'
|
||||
import { GetWorkflowFromNameClientTool } from '@/lib/copilot/tools/client/workflow/get-workflow-from-name'
|
||||
import { ListUserWorkflowsClientTool } from '@/lib/copilot/tools/client/workflow/list-user-workflows'
|
||||
import { RunWorkflowClientTool } from '@/lib/copilot/tools/client/workflow/run-workflow'
|
||||
@@ -84,21 +80,18 @@ const CLIENT_TOOL_INSTANTIATORS: Record<string, (id: string) => any> = {
|
||||
search_errors: (id) => new SearchErrorsClientTool(id),
|
||||
remember_debug: (id) => new RememberDebugClientTool(id),
|
||||
set_environment_variables: (id) => new SetEnvironmentVariablesClientTool(id),
|
||||
list_gdrive_files: (id) => new ListGDriveFilesClientTool(id),
|
||||
read_gdrive_file: (id) => new ReadGDriveFileClientTool(id),
|
||||
get_credentials: (id) => new GetCredentialsClientTool(id),
|
||||
knowledge_base: (id) => new KnowledgeBaseClientTool(id),
|
||||
make_api_request: (id) => new MakeApiRequestClientTool(id),
|
||||
plan: (id) => new PlanClientTool(id),
|
||||
checkoff_todo: (id) => new CheckoffTodoClientTool(id),
|
||||
mark_todo_in_progress: (id) => new MarkTodoInProgressClientTool(id),
|
||||
gdrive_request_access: (id) => new GDriveRequestAccessClientTool(id),
|
||||
oauth_request_access: (id) => new OAuthRequestAccessClientTool(id),
|
||||
edit_workflow: (id) => new EditWorkflowClientTool(id),
|
||||
get_user_workflow: (id) => new GetUserWorkflowClientTool(id),
|
||||
list_user_workflows: (id) => new ListUserWorkflowsClientTool(id),
|
||||
get_workflow_from_name: (id) => new GetWorkflowFromNameClientTool(id),
|
||||
get_global_workflow_variables: (id) => new GetGlobalWorkflowVariablesClientTool(id),
|
||||
get_workflow_data: (id) => new GetWorkflowDataClientTool(id),
|
||||
set_global_workflow_variables: (id) => new SetGlobalWorkflowVariablesClientTool(id),
|
||||
get_trigger_examples: (id) => new GetTriggerExamplesClientTool(id),
|
||||
get_examples_rag: (id) => new GetExamplesRagClientTool(id),
|
||||
@@ -107,7 +100,6 @@ const CLIENT_TOOL_INSTANTIATORS: Record<string, (id: string) => any> = {
|
||||
deploy_workflow: (id) => new DeployWorkflowClientTool(id),
|
||||
check_deployment_status: (id) => new CheckDeploymentStatusClientTool(id),
|
||||
navigate_ui: (id) => new NavigateUIClientTool(id),
|
||||
generate_diagram: (id) => new GenerateDiagramClientTool(id),
|
||||
}
|
||||
|
||||
// Read-only static metadata for class-based tools (no instances)
|
||||
@@ -123,20 +115,17 @@ export const CLASS_TOOL_METADATA: Record<string, BaseClientToolMetadata | undefi
|
||||
search_errors: (SearchErrorsClientTool as any)?.metadata,
|
||||
remember_debug: (RememberDebugClientTool as any)?.metadata,
|
||||
set_environment_variables: (SetEnvironmentVariablesClientTool as any)?.metadata,
|
||||
list_gdrive_files: (ListGDriveFilesClientTool as any)?.metadata,
|
||||
read_gdrive_file: (ReadGDriveFileClientTool as any)?.metadata,
|
||||
get_credentials: (GetCredentialsClientTool as any)?.metadata,
|
||||
knowledge_base: (KnowledgeBaseClientTool as any)?.metadata,
|
||||
make_api_request: (MakeApiRequestClientTool as any)?.metadata,
|
||||
plan: (PlanClientTool as any)?.metadata,
|
||||
checkoff_todo: (CheckoffTodoClientTool as any)?.metadata,
|
||||
mark_todo_in_progress: (MarkTodoInProgressClientTool as any)?.metadata,
|
||||
gdrive_request_access: (GDriveRequestAccessClientTool as any)?.metadata,
|
||||
edit_workflow: (EditWorkflowClientTool as any)?.metadata,
|
||||
get_user_workflow: (GetUserWorkflowClientTool as any)?.metadata,
|
||||
list_user_workflows: (ListUserWorkflowsClientTool as any)?.metadata,
|
||||
get_workflow_from_name: (GetWorkflowFromNameClientTool as any)?.metadata,
|
||||
get_global_workflow_variables: (GetGlobalWorkflowVariablesClientTool as any)?.metadata,
|
||||
get_workflow_data: (GetWorkflowDataClientTool as any)?.metadata,
|
||||
set_global_workflow_variables: (SetGlobalWorkflowVariablesClientTool as any)?.metadata,
|
||||
get_trigger_examples: (GetTriggerExamplesClientTool as any)?.metadata,
|
||||
get_examples_rag: (GetExamplesRagClientTool as any)?.metadata,
|
||||
@@ -146,7 +135,6 @@ export const CLASS_TOOL_METADATA: Record<string, BaseClientToolMetadata | undefi
|
||||
deploy_workflow: (DeployWorkflowClientTool as any)?.metadata,
|
||||
check_deployment_status: (CheckDeploymentStatusClientTool as any)?.metadata,
|
||||
navigate_ui: (NavigateUIClientTool as any)?.metadata,
|
||||
generate_diagram: (GenerateDiagramClientTool as any)?.metadata,
|
||||
}
|
||||
|
||||
function ensureClientToolInstance(toolName: string | undefined, toolCallId: string | undefined) {
|
||||
|
||||
@@ -105,6 +105,40 @@ function regenerateIds(workflowState: WorkflowState): WorkflowState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize subblock values by converting empty strings to null.
|
||||
* This provides backwards compatibility for workflows exported before the null sanitization fix,
|
||||
* preventing Zod validation errors like "Expected array, received string".
|
||||
*/
|
||||
function normalizeSubblockValues(blocks: Record<string, any>): Record<string, any> {
|
||||
const normalizedBlocks: Record<string, any> = {}
|
||||
|
||||
Object.entries(blocks).forEach(([blockId, block]) => {
|
||||
const normalizedBlock = { ...block }
|
||||
|
||||
if (block.subBlocks) {
|
||||
const normalizedSubBlocks: Record<string, any> = {}
|
||||
|
||||
Object.entries(block.subBlocks).forEach(([subBlockId, subBlock]: [string, any]) => {
|
||||
const normalizedSubBlock = { ...subBlock }
|
||||
|
||||
// Convert empty strings to null for consistency
|
||||
if (normalizedSubBlock.value === '') {
|
||||
normalizedSubBlock.value = null
|
||||
}
|
||||
|
||||
normalizedSubBlocks[subBlockId] = normalizedSubBlock
|
||||
})
|
||||
|
||||
normalizedBlock.subBlocks = normalizedSubBlocks
|
||||
}
|
||||
|
||||
normalizedBlocks[blockId] = normalizedBlock
|
||||
})
|
||||
|
||||
return normalizedBlocks
|
||||
}
|
||||
|
||||
export function parseWorkflowJson(
|
||||
jsonContent: string,
|
||||
regenerateIdsFlag = true
|
||||
@@ -203,9 +237,13 @@ export function parseWorkflowJson(
|
||||
return { data: null, errors }
|
||||
}
|
||||
|
||||
// Normalize non-string subblock values (convert empty strings to null)
|
||||
// This handles exported workflows that may have empty strings for non-string types
|
||||
const normalizedBlocks = normalizeSubblockValues(workflowData.blocks || {})
|
||||
|
||||
// Construct the workflow state with defaults
|
||||
let workflowState: WorkflowState = {
|
||||
blocks: workflowData.blocks || {},
|
||||
blocks: normalizedBlocks,
|
||||
edges: workflowData.edges || [],
|
||||
loops: workflowData.loops || {},
|
||||
parallels: workflowData.parallels || {},
|
||||
|
||||
158
bun.lock
158
bun.lock
@@ -14,7 +14,6 @@
|
||||
"drizzle-orm": "^0.44.5",
|
||||
"ffmpeg-static": "5.3.0",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"mermaid": "11.12.2",
|
||||
"mongodb": "6.19.0",
|
||||
"neo4j-driver": "6.0.1",
|
||||
"nodemailer": "7.0.11",
|
||||
@@ -142,7 +141,6 @@
|
||||
"lodash": "4.17.21",
|
||||
"lucide-react": "^0.479.0",
|
||||
"mammoth": "^1.9.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"mysql2": "3.14.3",
|
||||
"nanoid": "^3.3.7",
|
||||
"next": "16.0.7",
|
||||
@@ -303,8 +301,6 @@
|
||||
|
||||
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
||||
|
||||
"@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
|
||||
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.39.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-eMyDIPRZbt1CCLErRCi3exlAvNkBtRe+kW5vvJyef93PmNr/clstYgHhtvmkxN82nlKgzyGPCyGxrm0JQ1ZIdg=="],
|
||||
|
||||
"@ark/schema": ["@ark/schema@0.56.0", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA=="],
|
||||
@@ -509,8 +505,6 @@
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.0-beta.5", "", { "os": "win32", "cpu": "x64" }, "sha512-N7Yby52BJmvEdst1iMbclE5hxxefboaXKRJLm1tLfBYr4FeuoCe6j8HdiQSwhCRdIUGFFqBLaDXh//LLF6EReA=="],
|
||||
|
||||
"@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.1", "", {}, "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw=="],
|
||||
|
||||
"@browserbasehq/sdk": ["@browserbasehq/sdk@2.6.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA=="],
|
||||
|
||||
"@browserbasehq/stagehand": ["@browserbasehq/stagehand@2.5.4", "", { "dependencies": { "@anthropic-ai/sdk": "0.39.0", "@browserbasehq/sdk": "^2.4.0", "@google/genai": "^1.22.0", "@modelcontextprotocol/sdk": "^1.17.2", "ai": "^4.3.9", "devtools-protocol": "^0.0.1464554", "fetch-cookie": "^3.1.0", "openai": "^4.87.1", "pino": "^9.6.0", "pino-pretty": "^13.0.0", "playwright": "^1.52.0", "ws": "^8.18.0", "zod-to-json-schema": "^3.23.5" }, "optionalDependencies": { "@ai-sdk/anthropic": "^1.2.6", "@ai-sdk/azure": "^1.3.19", "@ai-sdk/cerebras": "^0.2.6", "@ai-sdk/deepseek": "^0.2.13", "@ai-sdk/google": "^1.2.6", "@ai-sdk/groq": "^1.2.4", "@ai-sdk/mistral": "^1.2.7", "@ai-sdk/openai": "^1.0.14", "@ai-sdk/perplexity": "^1.1.7", "@ai-sdk/togetherai": "^0.2.6", "@ai-sdk/xai": "^1.2.15", "ollama-ai-provider": "^1.2.0" }, "peerDependencies": { "deepmerge": "^4.3.1", "dotenv": "^16.4.5", "zod": ">=3.25.0 <3.25.68" }, "bin": { "evals": "dist/evals/cli.js" } }, "sha512-uUPOa+9i0JuWnczEwwP+Vc4Qfz98maxgKfQZVOGPmFQde9DSPTtfw9/ujB3+tnXy0SGkPl6K4lJInBljdxTdZQ=="],
|
||||
@@ -521,16 +515,6 @@
|
||||
|
||||
"@cerebras/cerebras_cloud_sdk": ["@cerebras/cerebras_cloud_sdk@1.59.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-bDx86IgIb+FzbLQVUtVnjsEv7e+TDPwQkulycLNImsb9ecyIJPfpURSIyxV4pdoHZ9zMVaQy59hBqJ0zLqNK8Q=="],
|
||||
|
||||
"@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@11.0.3", "", { "dependencies": { "@chevrotain/gast": "11.0.3", "@chevrotain/types": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ=="],
|
||||
|
||||
"@chevrotain/gast": ["@chevrotain/gast@11.0.3", "", { "dependencies": { "@chevrotain/types": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q=="],
|
||||
|
||||
"@chevrotain/regexp-to-ast": ["@chevrotain/regexp-to-ast@11.0.3", "", {}, "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA=="],
|
||||
|
||||
"@chevrotain/types": ["@chevrotain/types@11.0.3", "", {}, "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ=="],
|
||||
|
||||
"@chevrotain/utils": ["@chevrotain/utils@11.0.3", "", {}, "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ=="],
|
||||
|
||||
"@connectrpc/connect": ["@connectrpc/connect@2.0.0-rc.3", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.2.0" } }, "sha512-ARBt64yEyKbanyRETTjcjJuHr2YXorzQo0etyS5+P6oSeW8xEuzajA9g+zDnMcj1hlX2dQE93foIWQGfpru7gQ=="],
|
||||
|
||||
"@connectrpc/connect-web": ["@connectrpc/connect-web@2.0.0-rc.3", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.2.0", "@connectrpc/connect": "2.0.0-rc.3" } }, "sha512-w88P8Lsn5CCsA7MFRl2e6oLY4J/5toiNtJns/YJrlyQaWOy3RO8pDgkz+iIkG98RPMhj2thuBvsd3Cn4DKKCkw=="],
|
||||
@@ -637,10 +621,6 @@
|
||||
|
||||
"@hookform/resolvers": ["@hookform/resolvers@4.1.3", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.0.0" } }, "sha512-Jsv6UOWYTrEFJ/01ZrnwVXs7KDvP8XIo115i++5PWvNkNvkrsTfGiLS6w+eJ57CYtUtDQalUWovCZDHFJ8u1VQ=="],
|
||||
|
||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||
|
||||
"@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
|
||||
|
||||
"@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="],
|
||||
|
||||
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg=="],
|
||||
@@ -727,8 +707,6 @@
|
||||
|
||||
"@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="],
|
||||
|
||||
"@mermaid-js/parser": ["@mermaid-js/parser@0.6.3", "", { "dependencies": { "langium": "3.3.1" } }, "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA=="],
|
||||
|
||||
"@microsoft/fetch-event-source": ["@microsoft/fetch-event-source@2.0.1", "", {}, "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA=="],
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.20.2", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg=="],
|
||||
@@ -1457,8 +1435,6 @@
|
||||
|
||||
"@types/tough-cookie": ["@types/tough-cookie@4.0.5", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="],
|
||||
|
||||
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
|
||||
|
||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
|
||||
"@types/webidl-conversions": ["@types/webidl-conversions@7.0.3", "", {}, "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="],
|
||||
@@ -1573,7 +1549,7 @@
|
||||
|
||||
"base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.2", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-PxSsosKQjI38iXkmb3d0Y32efqyA0uW4s41u4IVBsLlWLhCiYNpH/AfNOVWRqCQBlD8TFJTz6OUWNd4DFJCnmw=="],
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.3", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-8QdH6czo+G7uBsNo0GiUfouPN1lRzKdJTGnKXwe12gkFbnnOUaUKGN55dMkfy+mnxmvjwl9zcI4VncczcVXDhA=="],
|
||||
|
||||
"basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="],
|
||||
|
||||
@@ -1667,10 +1643,6 @@
|
||||
|
||||
"cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="],
|
||||
|
||||
"chevrotain": ["chevrotain@11.0.3", "", { "dependencies": { "@chevrotain/cst-dts-gen": "11.0.3", "@chevrotain/gast": "11.0.3", "@chevrotain/regexp-to-ast": "11.0.3", "@chevrotain/types": "11.0.3", "@chevrotain/utils": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw=="],
|
||||
|
||||
"chevrotain-allstar": ["chevrotain-allstar@0.3.1", "", { "dependencies": { "lodash-es": "^4.17.21" }, "peerDependencies": { "chevrotain": "^11.0.0" } }, "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw=="],
|
||||
|
||||
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||
|
||||
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
||||
@@ -1753,8 +1725,6 @@
|
||||
|
||||
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
|
||||
|
||||
"cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="],
|
||||
|
||||
"cpu-features": ["cpu-features@0.0.10", "", { "dependencies": { "buildcheck": "~0.0.6", "nan": "^2.19.0" } }, "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA=="],
|
||||
|
||||
"crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
|
||||
@@ -1791,78 +1761,24 @@
|
||||
|
||||
"csv-parse": ["csv-parse@6.1.0", "", {}, "sha512-CEE+jwpgLn+MmtCpVcPtiCZpVtB6Z2OKPTr34pycYYoL7sxdOkXDdQ4lRiw6ioC0q6BLqhc6cKweCVvral8yhw=="],
|
||||
|
||||
"cytoscape": ["cytoscape@3.33.1", "", {}, "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ=="],
|
||||
|
||||
"cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="],
|
||||
|
||||
"cytoscape-fcose": ["cytoscape-fcose@2.2.0", "", { "dependencies": { "cose-base": "^2.2.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ=="],
|
||||
|
||||
"d3": ["d3@7.9.0", "", { "dependencies": { "d3-array": "3", "d3-axis": "3", "d3-brush": "3", "d3-chord": "3", "d3-color": "3", "d3-contour": "4", "d3-delaunay": "6", "d3-dispatch": "3", "d3-drag": "3", "d3-dsv": "3", "d3-ease": "3", "d3-fetch": "3", "d3-force": "3", "d3-format": "3", "d3-geo": "3", "d3-hierarchy": "3", "d3-interpolate": "3", "d3-path": "3", "d3-polygon": "3", "d3-quadtree": "3", "d3-random": "3", "d3-scale": "4", "d3-scale-chromatic": "3", "d3-selection": "3", "d3-shape": "3", "d3-time": "3", "d3-time-format": "4", "d3-timer": "3", "d3-transition": "3", "d3-zoom": "3" } }, "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA=="],
|
||||
|
||||
"d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
|
||||
|
||||
"d3-axis": ["d3-axis@3.0.0", "", {}, "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="],
|
||||
|
||||
"d3-brush": ["d3-brush@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "3", "d3-transition": "3" } }, "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ=="],
|
||||
|
||||
"d3-chord": ["d3-chord@3.0.1", "", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="],
|
||||
|
||||
"d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
|
||||
|
||||
"d3-contour": ["d3-contour@4.0.2", "", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="],
|
||||
|
||||
"d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="],
|
||||
|
||||
"d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="],
|
||||
|
||||
"d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="],
|
||||
|
||||
"d3-dsv": ["d3-dsv@3.0.1", "", { "dependencies": { "commander": "7", "iconv-lite": "0.6", "rw": "1" }, "bin": { "csv2json": "bin/dsv2json.js", "csv2tsv": "bin/dsv2dsv.js", "dsv2dsv": "bin/dsv2dsv.js", "dsv2json": "bin/dsv2json.js", "json2csv": "bin/json2dsv.js", "json2dsv": "bin/json2dsv.js", "json2tsv": "bin/json2dsv.js", "tsv2csv": "bin/dsv2dsv.js", "tsv2json": "bin/dsv2json.js" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="],
|
||||
|
||||
"d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
|
||||
|
||||
"d3-fetch": ["d3-fetch@3.0.1", "", { "dependencies": { "d3-dsv": "1 - 3" } }, "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw=="],
|
||||
|
||||
"d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="],
|
||||
|
||||
"d3-format": ["d3-format@3.1.0", "", {}, "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="],
|
||||
|
||||
"d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="],
|
||||
|
||||
"d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="],
|
||||
|
||||
"d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
|
||||
|
||||
"d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
|
||||
|
||||
"d3-polygon": ["d3-polygon@3.0.1", "", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="],
|
||||
|
||||
"d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="],
|
||||
|
||||
"d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="],
|
||||
|
||||
"d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="],
|
||||
|
||||
"d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
|
||||
|
||||
"d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="],
|
||||
|
||||
"d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="],
|
||||
|
||||
"d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
|
||||
|
||||
"d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
|
||||
|
||||
"d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
|
||||
|
||||
"d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
|
||||
|
||||
"d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="],
|
||||
|
||||
"d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="],
|
||||
|
||||
"dagre-d3-es": ["dagre-d3-es@7.0.13", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q=="],
|
||||
|
||||
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
||||
|
||||
"data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="],
|
||||
@@ -1891,8 +1807,6 @@
|
||||
|
||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
|
||||
"delaunator": ["delaunator@5.0.1", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="],
|
||||
|
||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
|
||||
@@ -1933,8 +1847,6 @@
|
||||
|
||||
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||
|
||||
"dompurify": ["dompurify@3.3.0", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ=="],
|
||||
|
||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||
|
||||
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||
@@ -1961,7 +1873,7 @@
|
||||
|
||||
"effect": ["effect@3.18.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.265", "", {}, "sha512-B7IkLR1/AE+9jR2LtVF/1/6PFhY5TlnEHnlrKmGk7PvkJibg5jr+mLXLLzq3QYl6PA1T/vLDthQPqIPAlS/PPA=="],
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.266", "", {}, "sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||
|
||||
@@ -2181,8 +2093,6 @@
|
||||
|
||||
"gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
|
||||
|
||||
"hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
@@ -2259,8 +2169,6 @@
|
||||
|
||||
"inquirer": ["inquirer@8.2.7", "", { "dependencies": { "@inquirer/external-editor": "^1.0.0", "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", "wrap-ansi": "^6.0.1" } }, "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA=="],
|
||||
|
||||
"internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
|
||||
|
||||
"ioredis": ["ioredis@5.8.2", "", { "dependencies": { "@ioredis/commands": "1.4.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q=="],
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
@@ -2361,20 +2269,12 @@
|
||||
|
||||
"jwt-decode": ["jwt-decode@4.0.0", "", {}, "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="],
|
||||
|
||||
"katex": ["katex@0.16.25", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q=="],
|
||||
|
||||
"khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="],
|
||||
|
||||
"kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
|
||||
|
||||
"kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
|
||||
|
||||
"kysely": ["kysely@0.28.8", "", {}, "sha512-QUOgl5ZrS9IRuhq5FvOKFSsD/3+IA6MLE81/bOOTRA/YQpKDza2sFdN5g6JCB9BOpqMJDGefLCQ9F12hRS13TA=="],
|
||||
|
||||
"langium": ["langium@3.3.1", "", { "dependencies": { "chevrotain": "~11.0.3", "chevrotain-allstar": "~0.3.0", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", "vscode-uri": "~3.0.8" } }, "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w=="],
|
||||
|
||||
"layout-base": ["layout-base@1.0.2", "", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="],
|
||||
|
||||
"leac": ["leac@0.6.0", "", {}, "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="],
|
||||
|
||||
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
||||
@@ -2415,8 +2315,6 @@
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
|
||||
|
||||
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
|
||||
|
||||
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
|
||||
@@ -2469,7 +2367,7 @@
|
||||
|
||||
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
|
||||
|
||||
"marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
|
||||
"marked": ["marked@7.0.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
@@ -2517,8 +2415,6 @@
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
|
||||
"mermaid": ["mermaid@11.12.2", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.1", "@mermaid-js/parser": "^0.6.3", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.13", "dayjs": "^1.11.18", "dompurify": "^3.2.5", "katex": "^0.16.22", "khroma": "^2.1.0", "lodash-es": "^4.17.21", "marked": "^16.2.1", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w=="],
|
||||
|
||||
"meshoptimizer": ["meshoptimizer@0.18.1", "", {}, "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw=="],
|
||||
|
||||
"methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="],
|
||||
@@ -2741,8 +2637,6 @@
|
||||
|
||||
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
|
||||
|
||||
"package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
|
||||
|
||||
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
||||
|
||||
"papaparse": ["papaparse@5.5.3", "", {}, "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A=="],
|
||||
@@ -2765,8 +2659,6 @@
|
||||
|
||||
"partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="],
|
||||
|
||||
"path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="],
|
||||
|
||||
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
@@ -2819,10 +2711,6 @@
|
||||
|
||||
"playwright-core": ["playwright-core@1.57.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ=="],
|
||||
|
||||
"points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="],
|
||||
|
||||
"points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="],
|
||||
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
|
||||
"postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
|
||||
@@ -2991,14 +2879,10 @@
|
||||
|
||||
"rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
|
||||
|
||||
"robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
|
||||
|
||||
"rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="],
|
||||
|
||||
"rou3": ["rou3@0.5.1", "", {}, "sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ=="],
|
||||
|
||||
"roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
|
||||
|
||||
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
||||
|
||||
"rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="],
|
||||
@@ -3009,8 +2893,6 @@
|
||||
|
||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||
|
||||
"rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
|
||||
|
||||
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
@@ -3171,8 +3053,6 @@
|
||||
|
||||
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
|
||||
|
||||
"stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="],
|
||||
|
||||
"sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="],
|
||||
|
||||
"superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="],
|
||||
@@ -3247,8 +3127,6 @@
|
||||
|
||||
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
||||
|
||||
"ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="],
|
||||
|
||||
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
|
||||
|
||||
"tsafe": ["tsafe@1.8.12", "", {}, "sha512-nFRqW0ttu/2o6XTXsHiVZWJBCOaxhVqZLg7dgs3coZNsCMPXPfwz+zPHAQA+70fNnVJLAPg1EgGIqK9Q84tvAw=="],
|
||||
@@ -3361,18 +3239,10 @@
|
||||
|
||||
"vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
|
||||
|
||||
"vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
|
||||
|
||||
"vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="],
|
||||
|
||||
"vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="],
|
||||
|
||||
"vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="],
|
||||
|
||||
"vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="],
|
||||
|
||||
"vscode-uri": ["vscode-uri@3.0.8", "", {}, "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="],
|
||||
|
||||
"w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="],
|
||||
|
||||
"wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="],
|
||||
@@ -3733,8 +3603,6 @@
|
||||
|
||||
"c12/confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
|
||||
|
||||
"c12/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||
|
||||
"c12/pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
|
||||
|
||||
"cheerio/htmlparser2": ["htmlparser2@10.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.1", "entities": "^6.0.0" } }, "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g=="],
|
||||
@@ -3753,14 +3621,6 @@
|
||||
|
||||
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="],
|
||||
|
||||
"d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
|
||||
|
||||
"d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="],
|
||||
|
||||
"d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="],
|
||||
|
||||
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"ecdsa-sig-formatter/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
@@ -3827,8 +3687,6 @@
|
||||
|
||||
"jws/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
|
||||
|
||||
"linebreak/base64-js": ["base64-js@0.0.8", "", {}, "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="],
|
||||
|
||||
"lint-staged/commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="],
|
||||
@@ -3849,8 +3707,6 @@
|
||||
|
||||
"mammoth/xmlbuilder": ["xmlbuilder@10.1.1", "", {}, "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg=="],
|
||||
|
||||
"md-to-react-email/marked": ["marked@7.0.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ=="],
|
||||
|
||||
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
@@ -3969,8 +3825,6 @@
|
||||
|
||||
"unist-util-remove/unist-util-visit-parents": ["unist-util-visit-parents@5.1.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" } }, "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg=="],
|
||||
|
||||
"vite-tsconfig-paths/tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="],
|
||||
|
||||
"vitest/tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
|
||||
|
||||
"xml-crypto/xpath": ["xpath@0.0.33", "", {}, "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA=="],
|
||||
@@ -4141,12 +3995,6 @@
|
||||
|
||||
"critters/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="],
|
||||
|
||||
"d3-sankey/d3-array/internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="],
|
||||
|
||||
"d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="],
|
||||
|
||||
"engine.io/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
|
||||
|
||||
"form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
"drizzle-orm": "^0.44.5",
|
||||
"ffmpeg-static": "5.3.0",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"mermaid": "11.12.2",
|
||||
"mongodb": "6.19.0",
|
||||
"neo4j-driver": "6.0.1",
|
||||
"nodemailer": "7.0.11",
|
||||
|
||||
Reference in New Issue
Block a user