UNPKG

@vfarcic/dot-ai

Version:

AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance

624 lines (623 loc) 27.1 kB
"use strict"; /** * Core Policy Operations * * Handles policy intent management operations including CRUD operations * and Kyverno cluster policy cleanup */ Object.defineProperty(exports, "__esModule", { value: true }); exports.getPolicyService = getPolicyService; exports.findKyvernoPoliciesByPolicyId = findKyvernoPoliciesByPolicyId; exports.findAllKyvernoPoliciesForPolicyIntents = findAllKyvernoPoliciesForPolicyIntents; exports.deleteKyvernoPoliciesByPolicyId = deleteKyvernoPoliciesByPolicyId; exports.deleteAllKyvernoPoliciesForPolicyIntents = deleteAllKyvernoPoliciesForPolicyIntents; exports.handlePolicyDelete = handlePolicyDelete; exports.handlePolicyDeleteAll = handlePolicyDeleteAll; exports.handlePolicyOperation = handlePolicyOperation; const error_handling_1 = require("./error-handling"); const policy_vector_service_1 = require("./policy-vector-service"); const unified_creation_session_1 = require("./unified-creation-session"); const plugin_registry_1 = require("./plugin-registry"); const validation_1 = require("./constants/validation"); const constants_1 = require("./constants"); /** * Get initialized policy service */ async function getPolicyService() { const policyService = new policy_vector_service_1.PolicyVectorService(); // Always ensure proper collection initialization try { await policyService.initialize(); } catch (error) { // If initialization fails, try to provide helpful error context const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Vector DB collection initialization failed: ${errorMessage}. This may be due to dimension mismatch or collection configuration issues.`, { cause: error }); } return policyService; } /** * Execute kubectl command via plugin * PRD #343: All kubectl operations go through plugin * PRD #359: Uses unified plugin registry */ async function executeKubectlViaPlugin(args) { const response = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'kubectl_exec_command', { args }); if (response.success) { if (typeof response.result === 'object' && response.result !== null) { const result = response.result; // Check for nested error - plugin wraps kubectl errors in { success: false, error: "..." } if (result.success === false) { throw new Error(result.error || result.message || 'kubectl command failed'); } // Return only the data field - never pass JSON wrapper to consumers if (result.data !== undefined) { return String(result.data); } if (typeof result === 'string') { return result; } throw new Error('Plugin returned unexpected response format - missing data field'); } return String(response.result || ''); } else { throw new Error(response.error?.message || 'kubectl command failed via plugin'); } } /** * Find Kyverno policies by policy intent ID using label selector * PRD #359: Uses unified plugin registry */ async function findKyvernoPoliciesByPolicyId(policyId, logger, requestId) { try { logger.info('Searching for Kyverno policies by policy ID', { requestId, policyId, }); const output = await executeKubectlViaPlugin([ 'get', 'clusterpolicy', '-l', `policy-intent/id=${policyId}`, '-o', 'json', ]); const parsedOutput = JSON.parse(output || '{"items": []}'); const policies = parsedOutput.items || []; logger.info('Found Kyverno policies for policy intent', { requestId, policyId, policyCount: policies.length, policyNames: policies.map((p) => p.metadata?.name), }); return policies.map((p) => ({ name: p.metadata?.name || '', labels: p.metadata?.labels, creationTimestamp: p.metadata?.creationTimestamp, })); } catch (error) { logger.warn('Failed to query Kyverno policies (cluster may not have Kyverno or no policies found)', { requestId, policyId, error: error instanceof Error ? error.message : String(error), }); return []; } } /** * Find all Kyverno policies that have policy-intent/id labels * PRD #359: Uses unified plugin registry */ async function findAllKyvernoPoliciesForPolicyIntents(logger, requestId) { try { logger.info('Searching for all Kyverno policies with policy-intent labels', { requestId, }); const output = await executeKubectlViaPlugin([ 'get', 'clusterpolicy', '-l', 'policy-intent/id', '-o', 'json', ]); const parsedOutput = JSON.parse(output || '{"items": []}'); const policies = parsedOutput.items || []; logger.info('Found all Kyverno policies for policy intents', { requestId, policyCount: policies.length, policyNames: policies.map((p) => p.metadata?.name), }); return policies.map((p) => ({ name: p.metadata?.name || '', policyId: p.metadata?.labels?.['policy-intent/id'], labels: p.metadata?.labels, creationTimestamp: p.metadata?.creationTimestamp, })); } catch (error) { logger.warn('Failed to query all Kyverno policies (cluster may not have Kyverno or no policies found)', { requestId, error: error instanceof Error ? error.message : String(error), }); return []; } } /** * Delete Kyverno policies by policy intent ID using label selector * PRD #359: Uses unified plugin registry */ async function deleteKyvernoPoliciesByPolicyId(policyId, logger, requestId) { try { logger.info('Deleting Kyverno policies by policy ID', { requestId, policyId, }); const output = await executeKubectlViaPlugin([ 'delete', 'clusterpolicy', '-l', `policy-intent/id=${policyId}`, ]); logger.info('Kyverno policies deleted successfully', { requestId, policyId, output, }); return { successful: [{ policyId, deletedAt: new Date().toISOString() }], failed: [], total: 1, }; } catch (error) { logger.error('Failed to delete Kyverno policies', error, { requestId, policyId, error: error instanceof Error ? error.message : String(error), }); return { successful: [], failed: [ { policyId, error: error instanceof Error ? error.message : String(error), }, ], total: 1, }; } } /** * Delete all Kyverno policies that have policy-intent/id labels * PRD #359: Uses unified plugin registry */ async function deleteAllKyvernoPoliciesForPolicyIntents(logger, requestId) { try { logger.info('Deleting all Kyverno policies with policy-intent labels', { requestId, }); const output = await executeKubectlViaPlugin([ 'delete', 'clusterpolicy', '-l', 'policy-intent/id', ]); logger.info('All Kyverno policies deleted successfully', { requestId, output, }); return { successful: [{ deletedAt: new Date().toISOString() }], failed: [], total: 1, }; } catch (error) { logger.error('Failed to delete all Kyverno policies', error, { requestId, error: error instanceof Error ? error.message : String(error), }); return { successful: [], failed: [ { error: error instanceof Error ? error.message : String(error) }, ], total: 1, }; } } /** * Handle individual policy delete with Kyverno cleanup * PRD #359: Uses unified plugin registry */ async function handlePolicyDelete(policyId, policyService, args, logger, requestId) { try { // Check if policy intent exists const existingPolicyIntent = await policyService.getPolicyIntent(policyId); if (!existingPolicyIntent) { return { success: false, operation: 'delete', dataType: 'policy', message: `Policy intent not found: ${policyId}`, error: 'Policy intent not found', }; } // Check if there are deployed Kyverno policies with this policy ID const kyvernoPolicies = await findKyvernoPoliciesByPolicyId(policyId, logger, requestId); if (kyvernoPolicies.length > 0 && !args.response) { // Show confirmation prompt for Kyverno cleanup return { success: true, operation: 'delete', dataType: 'policy', requiresConfirmation: true, message: 'Policy intent has deployed Kyverno policies that need cleanup decision', confirmation: { question: `Policy intent "${existingPolicyIntent.description.substring(0, 60)}..." has ${kyvernoPolicies.length} deployed Kyverno policies in your cluster: ${kyvernoPolicies.map(p => p.name).join(', ')}\n\n**Choose what to do:**\n\n1. **Delete everything** - Remove policy intent AND delete Kyverno policies from cluster\n2. **Keep Kyverno policies** - Remove policy intent only, preserve cluster policies\n\n⚠️ **Warning**: Option 1 will remove active policy enforcement from your cluster.\n\n**What would you like to do?**`, options: ['Delete everything', 'Keep Kyverno policies'], }, policyIntent: existingPolicyIntent, kyvernoPolicies: kyvernoPolicies, }; } // Process user's response or proceed with direct deletion let kyvernoCleanupResults = null; if (kyvernoPolicies.length > 0 && args.response) { const response = args.response.trim(); if (response === '1' || response.toLowerCase().includes('delete everything')) { // Delete Kyverno policies from cluster kyvernoCleanupResults = await deleteKyvernoPoliciesByPolicyId(policyId, logger, requestId); } } // Always delete the policy intent from Vector DB await policyService.deletePolicyIntent(policyId); const cleanupMessage = kyvernoCleanupResults ? `with Kyverno cleanup (${kyvernoCleanupResults.successful.length} deleted, ${kyvernoCleanupResults.failed.length} failed)` : kyvernoPolicies.length > 0 ? '(Kyverno policies preserved in cluster)' : '(no Kyverno policies to cleanup)'; return { success: true, operation: 'delete', dataType: 'policy', message: `Policy intent deleted successfully ${cleanupMessage}`, deletedPolicyIntent: existingPolicyIntent, kyvernoCleanup: kyvernoCleanupResults || { preserved: true }, }; } catch (error) { logger.error('Failed to delete policy intent', error, { requestId, policyId, }); return { success: false, operation: 'delete', dataType: 'policy', message: 'Failed to delete policy intent', error: error instanceof Error ? error.message : String(error), }; } } /** * Handle deleteAll policies with batch Kyverno cleanup * PRD #359: Uses unified plugin registry */ async function handlePolicyDeleteAll(policyService, args, logger, requestId) { try { // Get all policy intents const allPolicyIntents = await policyService.getAllPolicyIntents(); if (!allPolicyIntents || allPolicyIntents.length === 0) { return { success: true, operation: 'deleteAll', dataType: 'policy', message: 'No policy intents found to delete', deletedCount: 0, }; } // Find all deployed Kyverno policies for all policy intents const allKyvernoPolicies = await findAllKyvernoPoliciesForPolicyIntents(logger, requestId); if (allKyvernoPolicies.length > 0 && !args.response) { // Show confirmation prompt for batch Kyverno cleanup return { success: true, operation: 'deleteAll', dataType: 'policy', requiresConfirmation: true, message: 'Found policy intents with deployed Kyverno policies that need cleanup decision', confirmation: { question: `Deleting ${allPolicyIntents.length} policy intents. Found ${allKyvernoPolicies.length} deployed Kyverno policies in your cluster: ${allKyvernoPolicies.map(p => p.name).join(', ')}\n\n**Choose what to do:**\n\n1. **Delete everything** - Remove all policy intents AND delete all Kyverno policies from cluster\n2. **Keep Kyverno policies** - Remove all policy intents only, preserve all cluster policies\n\n⚠️ **Warning**: Option 1 will remove ALL active policy enforcement from your cluster.\n\n**What would you like to do?**`, options: ['Delete everything', 'Keep Kyverno policies'], }, policyIntents: allPolicyIntents, kyvernoPolicies: allKyvernoPolicies, }; } // Process user's response or proceed with direct deletion let kyvernoCleanupResults = null; if (allKyvernoPolicies.length > 0 && args.response) { const response = args.response.trim(); if (response === '1' || response.toLowerCase().includes('delete everything')) { // Delete all Kyverno policies from cluster kyvernoCleanupResults = await deleteAllKyvernoPoliciesForPolicyIntents(logger, requestId); } } // Always delete all policy intents from Vector DB for (const policyIntent of allPolicyIntents) { await policyService.deletePolicyIntent(policyIntent.id); } const cleanupMessage = kyvernoCleanupResults ? `with Kyverno cleanup (${kyvernoCleanupResults.successful.length} deleted, ${kyvernoCleanupResults.failed.length} failed)` : allKyvernoPolicies.length > 0 ? '(Kyverno policies preserved in cluster)' : '(no Kyverno policies to cleanup)'; return { success: true, operation: 'deleteAll', dataType: 'policy', message: `All ${allPolicyIntents.length} policy intents deleted successfully ${cleanupMessage}`, deletedCount: allPolicyIntents.length, deletedPolicyIntents: allPolicyIntents, kyvernoCleanup: kyvernoCleanupResults || { preserved: true }, }; } catch (error) { logger.error('Failed to delete all policy intents', error, { requestId, }); return { success: false, operation: 'deleteAll', dataType: 'policy', message: 'Failed to delete all policy intents', error: error instanceof Error ? error.message : String(error), }; } } /** * Main policy operations handler - delegates to specific operation functions * Requires shared validation utilities to be passed as parameters to avoid circular imports * PRD #359: Uses unified plugin registry for kubectl operations */ async function handlePolicyOperation(operation, args, logger, requestId, validateVectorDBConnection, validateEmbeddingService) { // Get policy service and validate Vector DB connection const policyService = await getPolicyService(); const connectionCheck = await validateVectorDBConnection(policyService, logger, requestId); if (!connectionCheck.success) { return { success: false, operation, dataType: 'policy', error: connectionCheck.error, message: 'Vector DB connection required for policy management', }; } // Validate embedding service and fail if unavailable (except for delete operations) const operationsRequiringEmbedding = ['create', 'search']; if (operationsRequiringEmbedding.includes(operation)) { const embeddingCheck = await validateEmbeddingService(logger, requestId); if (!embeddingCheck.success) { return { success: false, operation, dataType: 'policy', error: embeddingCheck.error, message: constants_1.AI_SERVICE_ERROR_TEMPLATES.OPENAI_KEY_REQUIRED('policy management'), }; } } const sessionManager = new unified_creation_session_1.UnifiedCreationSessionManager('policy'); switch (operation) { case 'create': { let workflowStep; if (args.sessionId) { // Continue existing session logger.info('Continuing policy creation workflow', { requestId, sessionId: args.sessionId, }); if (args.response) { // Process user response and move to next step const updatedSession = sessionManager.processResponse(args.sessionId, args.response); workflowStep = await sessionManager.getNextWorkflowStep(updatedSession, args); } else { // Just get current step without processing response const session = sessionManager.loadSession(args.sessionId); if (!session) { throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.HIGH, `Session not found: ${args.sessionId}`, { operation: 'policy_workflow_continue', component: 'OrganizationalDataTool', requestId, input: { sessionId: args.sessionId }, }); } workflowStep = await sessionManager.getNextWorkflowStep(session, args); } if (!workflowStep) { throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.HIGH, `Session not found or workflow failed`, { operation: 'policy_workflow_continue', component: 'OrganizationalDataTool', requestId, input: { sessionId: args.sessionId }, }); } } else { // Start new workflow session logger.info('Starting new policy creation workflow', { requestId }); const session = sessionManager.createSession(args); workflowStep = await sessionManager.getNextWorkflowStep(session, args); if (!workflowStep) { throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.OPERATION, error_handling_1.ErrorSeverity.HIGH, 'Failed to initialize policy creation workflow', { operation: 'policy_workflow_start', component: 'OrganizationalDataTool', requestId, }); } } // Always check if workflow is complete and store policy in Vector DB let storageInfo = {}; const isComplete = !('nextStep' in workflowStep) || !workflowStep.nextStep; // Complete when no next step const workflowData = workflowStep.data; const hasPolicy = !!workflowData?.policy; logger.info('Checking workflow completion', { requestId, nextStep: 'nextStep' in workflowStep ? workflowStep.nextStep : 'complete', hasPolicy, policyId: workflowData?.policy?.id, }); if (isComplete && hasPolicy) { try { await policyService.storePolicyIntent(workflowData.policy); storageInfo = { stored: true, collectionName: 'policies', policyId: workflowData.policy.id, }; logger.info('Policy stored in Vector DB successfully', { requestId, policyId: workflowData.policy.id, description: workflowData.policy.description.substring(0, 50) + (workflowData.policy.description.length > 50 ? '...' : ''), }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); storageInfo = { stored: false, error: errorMessage, policyId: workflowData.policy.id, }; logger.error('Failed to store policy in Vector DB', error instanceof Error ? error : new Error(String(error)), { requestId, policyId: workflowData.policy.id, }); } } // For completed policies, storage failure means creation failure const storageSucceeded = storageInfo.stored === true; const operationSucceeded = !isComplete || storageSucceeded; return { success: operationSucceeded, operation: 'create', dataType: 'policy', workflow: workflowStep, storage: storageInfo, message: isComplete ? storageSucceeded ? `Policy created and stored successfully` : 'Policy creation failed - storage error' : 'Workflow step ready', }; } case 'list': { const limit = args.limit || 10; const policyIntents = await policyService.getAllPolicyIntents(); const totalCount = await policyService.getPolicyIntentsCount(); const limitedPolicyIntents = policyIntents.slice(0, limit); return { success: true, operation, dataType: 'policy', message: `Found ${totalCount} policy intents (showing ${limitedPolicyIntents.length})`, policyIntents: limitedPolicyIntents, totalCount, note: totalCount > limit ? `Showing first ${limit} of ${totalCount} policy intents. Use limit parameter to see more.` : undefined, }; } case 'get': { if (!args.id) { return { success: false, operation, dataType: 'policy', message: 'Policy intent ID is required for get operation', error: validation_1.VALIDATION_MESSAGES.MISSING_PARAMETER('id'), }; } const policyIntent = await policyService.getPolicyIntent(args.id); if (!policyIntent) { return { success: false, operation, dataType: 'policy', message: `Policy intent not found: ${args.id}`, error: 'Policy intent not found', }; } return { success: true, operation, dataType: 'policy', message: 'Policy intent retrieved successfully', policyIntent, }; } case 'search': { if (!args.id) { // For search, 'id' parameter contains the search query return { success: false, operation, dataType: 'policy', message: 'Search query is required (use id parameter)', error: validation_1.VALIDATION_MESSAGES.MISSING_PARAMETER_WITH_CONTEXT('id', 'search query'), }; } const limit = args.limit || 10; const searchResults = await policyService.searchPolicyIntents(args.id, { limit, }); return { success: true, operation, dataType: 'policy', message: `Found ${searchResults.length} policy intents matching "${args.id}"`, policyIntents: searchResults.map(result => result.data), searchResults: searchResults.map(result => ({ policyIntent: result.data, score: result.score, })), }; } case 'delete': { if (!args.id) { return { success: false, operation, dataType: 'policy', message: 'Policy intent ID is required for delete operation', error: validation_1.VALIDATION_MESSAGES.MISSING_PARAMETER('id'), }; } if (!(0, plugin_registry_1.isPluginInitialized)()) { throw new Error('Plugin system not available. Policy delete requires agentic-tools plugin for kubectl operations.'); } return await handlePolicyDelete(args.id, policyService, args, logger, requestId); } case 'deleteAll': { if (!(0, plugin_registry_1.isPluginInitialized)()) { throw new Error('Plugin system not available. Policy deleteAll requires agentic-tools plugin for kubectl operations.'); } return await handlePolicyDeleteAll(policyService, args, logger, requestId); } default: return { success: false, operation, dataType: 'policy', message: `Unsupported operation: ${operation}. Supported operations: create, list, get, search, delete, deleteAll`, error: 'Unsupported operation', }; } }