UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

305 lines (304 loc) 9.39 kB
/** * workflow/core/workflowRegistry.ts * Registry for managing workflow configurations */ import { logger } from "../../utils/logger.js"; import { validateForRegistration } from "../utils/workflowValidation.js"; const functionTag = "WorkflowRegistry"; // ============================================================================ // REGISTRY STORAGE // ============================================================================ /** * In-memory workflow registry * TODO: Consider persistent storage in future phases */ const workflowRegistry = new Map(); // ============================================================================ // REGISTRY OPERATIONS // ============================================================================ /** * Register a new workflow * @param config - Workflow configuration to register * @param options - Registration options * @returns Registration result */ export function registerWorkflow(config, options = {}) { const { validateBeforeRegister = true, allowOverwrite = false } = options; logger.info(`[${functionTag}] Registering workflow`, { workflowId: config.id, name: config.name, type: config.type, }); // Validate if requested if (validateBeforeRegister) { const validation = validateForRegistration(config); if (!validation.valid) { logger.error(`[${functionTag}] Workflow validation failed`, { workflowId: config.id, errors: validation.errors, }); return { success: false, workflowId: config.id, validation, error: "Workflow validation failed", }; } } // Check for existing workflow if (workflowRegistry.has(config.id) && !allowOverwrite) { logger.warn(`[${functionTag}] Workflow already exists`, { workflowId: config.id, }); return { success: false, workflowId: config.id, error: "Workflow already exists. Set allowOverwrite=true to replace.", }; } // Register workflow const entry = { config: { ...config }, registeredAt: new Date().toISOString(), usageCount: 0, }; workflowRegistry.set(config.id, entry); logger.info(`[${functionTag}] Workflow registered successfully`, { workflowId: config.id, name: config.name, }); return { success: true, workflowId: config.id, }; } /** * Unregister a workflow * @param workflowId - ID of workflow to unregister * @returns True if workflow was unregistered */ export function unregisterWorkflow(workflowId) { const exists = workflowRegistry.has(workflowId); if (exists) { workflowRegistry.delete(workflowId); logger.info(`[${functionTag}] Workflow unregistered`, { workflowId }); } else { logger.warn(`[${functionTag}] Workflow not found for unregistration`, { workflowId, }); } return exists; } /** * Get workflow configuration by ID * @param workflowId - ID of workflow to retrieve * @returns Workflow configuration or undefined */ export function getWorkflow(workflowId) { const entry = workflowRegistry.get(workflowId); if (entry) { // Update last used timestamp entry.lastUsed = new Date().toISOString(); entry.usageCount++; logger.debug(`[${functionTag}] Workflow retrieved`, { workflowId, usageCount: entry.usageCount, }); return entry.config; } logger.warn(`[${functionTag}] Workflow not found`, { workflowId }); return undefined; } /** * Check if workflow exists * @param workflowId - ID of workflow to check * @returns True if workflow exists */ export function hasWorkflow(workflowId) { return workflowRegistry.has(workflowId); } /** * List all registered workflows * @param options - Listing options for filtering * @returns Array of workflow configurations */ export function listWorkflows(options = {}) { const { type, tags, limit, offset = 0 } = options; let workflows = Array.from(workflowRegistry.values()).map((entry) => entry.config); // Filter by type if (type) { workflows = workflows.filter((w) => w.type === type); } // Filter by tags if (tags && tags.length > 0) { workflows = workflows.filter((w) => { if (!w.tags || w.tags.length === 0) { return false; } return tags.some((tag) => w.tags?.includes(tag)); }); } // Apply pagination if (limit !== undefined) { workflows = workflows.slice(offset, offset + limit); } logger.debug(`[${functionTag}] Listed workflows`, { count: workflows.length, type, tags, }); return workflows; } /** * Get workflow metadata (usage stats, timestamps) * @param workflowId - ID of workflow * @returns Metadata or undefined */ export function getWorkflowMetadata(workflowId) { const entry = workflowRegistry.get(workflowId); if (!entry) { return undefined; } return { registeredAt: entry.registeredAt, lastUsed: entry.lastUsed, usageCount: entry.usageCount, }; } /** * Update workflow configuration * @param workflowId - ID of workflow to update * @param updates - Partial workflow config with updates * @param options - Update options * @returns Update result */ export function updateWorkflow(workflowId, updates, options = {}) { const existing = workflowRegistry.get(workflowId); if (!existing) { logger.warn(`[${functionTag}] Workflow not found for update`, { workflowId, }); return { success: false, workflowId, error: "Workflow not found", }; } // Merge updates with existing config const updatedConfig = { ...existing.config, ...updates, id: workflowId, // Ensure ID doesn't change updatedAt: new Date().toISOString(), }; // Validate if requested const { validateBeforeRegister = true } = options; if (validateBeforeRegister) { const validation = validateForRegistration(updatedConfig); if (!validation.valid) { logger.error(`[${functionTag}] Updated workflow validation failed`, { workflowId, errors: validation.errors, }); return { success: false, workflowId, validation, error: "Updated workflow validation failed", }; } } // Update registry entry existing.config = updatedConfig; logger.info(`[${functionTag}] Workflow updated successfully`, { workflowId, }); return { success: true, workflowId, }; } /** * Clear all workflows from registry * WARNING: This will remove all registered workflows */ export function clearRegistry() { const count = workflowRegistry.size; workflowRegistry.clear(); logger.info(`[${functionTag}] Registry cleared`, { workflowsRemoved: count, }); } /** * Get registry statistics * @returns Statistics about registered workflows */ export function getRegistryStats() { const entries = Array.from(workflowRegistry.values()); const byType = {}; let totalUsage = 0; let mostUsed; entries.forEach((entry) => { // Count by type const type = entry.config.type; byType[type] = (byType[type] || 0) + 1; // Sum usage totalUsage += entry.usageCount; // Track most used if (!mostUsed || entry.usageCount > mostUsed.count) { mostUsed = { id: entry.config.id, name: entry.config.name, count: entry.usageCount, }; } }); return { totalWorkflows: workflowRegistry.size, byType, totalUsage, mostUsed: mostUsed && mostUsed.count > 0 ? mostUsed : undefined, }; } /** * Export registry as JSON for backup/sharing * @returns JSON string of all workflows */ export function exportRegistry() { const workflows = Array.from(workflowRegistry.values()).map((entry) => entry.config); return JSON.stringify(workflows, null, 2); } /** * Import workflows from JSON * @param json - JSON string of workflow configs * @param options - Import options * @returns Array of registration results */ export function importRegistry(json, options = {}) { try { const workflows = JSON.parse(json); const results = []; workflows.forEach((config) => { const result = registerWorkflow(config, options); results.push(result); }); logger.info(`[${functionTag}] Registry import completed`, { total: workflows.length, successful: results.filter((r) => r.success).length, }); return results; } catch (error) { logger.error(`[${functionTag}] Registry import failed`, { error: error.message, }); return [ { success: false, workflowId: "import-error", error: `Import failed: ${error.message}`, }, ]; } }