UNPKG

permamind

Version:

An MCP server that provides an immortal memory layer for AI agents and clients

595 lines (574 loc) 21.7 kB
const service = (docsService, tealCompilerService, aoLiteTestService, deployService, tealWorkflowService) => { // In-memory pipeline storage (in production, this would be persistent) const pipelineStore = new Map(); return { cancelPipeline: async (pipelineId) => { const pipeline = pipelineStore.get(pipelineId); if (!pipeline) { throw new Error(`Pipeline not found: ${pipelineId}`); } pipeline.status = "failed"; pipeline.updatedAt = new Date(); pipelineStore.set(pipelineId, pipeline); }, createPipeline: async (name, processDefinition, configuration, processType) => { try { const tags = ["ao", "development", "pipeline"]; if (processType) { tags.push(processType); } const pipeline = { configuration, createdAt: new Date(), id: generatePipelineId(), metadata: { aoVersion: processDefinition.metadata.aoVersion, author: processDefinition.metadata.author, description: `Development pipeline for ${name}`, processType: "teal", tags, version: processDefinition.metadata.version, }, name, stages: await createDefaultStages(processDefinition), status: "draft", updatedAt: new Date(), }; pipelineStore.set(pipeline.id, pipeline); return pipeline; } catch (error) { throw new Error(`Failed to create pipeline: ${error instanceof Error ? error.message : "Unknown error"}`); } }, createQuickStartPipeline: async (processType, name, configuration = {}) => { try { // Create a basic process definition for the quick start const processDefinition = { compiledLua: "", dependencies: [], id: generateProcessId(), metadata: { aoVersion: "2.0.0", author: "QuickStart", compileOptions: { strict: true, target: "lua53", warnings: true, }, description: `Quick start ${processType} process`, version: "1.0.0", }, name, source: getQuickStartTemplate(processType), typeDefinitions: [], version: "1.0.0", }; // Create pipeline with default configuration const defaultConfig = { autoAdvance: true, parallelExecution: false, retries: 3, stopOnError: true, timeout: 300000, // 5 minutes ...configuration, }; return await service(docsService, tealCompilerService, aoLiteTestService, deployService, tealWorkflowService).createPipeline(name, processDefinition, defaultConfig, processType); } catch (error) { throw new Error(`Failed to create quick start pipeline: ${error instanceof Error ? error.message : "Unknown error"}`); } }, executePipeline: async (pipeline, signer) => { const startTime = Date.now(); const stageResults = []; const artifacts = []; try { // Update pipeline status pipeline.status = "running"; pipeline.updatedAt = new Date(); pipelineStore.set(pipeline.id, pipeline); // Execute stages for (const stage of pipeline.stages) { try { const stageResult = await service(docsService, tealCompilerService, aoLiteTestService, deployService, tealWorkflowService).executeStage(stage, pipeline, signer); stageResults.push(stageResult); // Collect artifacts if (stageResult.artifacts) { artifacts.push(...stageResult.artifacts); } // Stop on failure if configured if (stageResult.status === "failed" && pipeline.configuration.stopOnError) { break; } } catch (error) { const failedResult = { duration: 0, error: error instanceof Error ? error.message : "Unknown error", name: stage.name, stageId: stage.id, status: "failed", }; stageResults.push(failedResult); if (pipeline.configuration.stopOnError) { break; } } } // Calculate results const totalStages = pipeline.stages.length; const completedStages = stageResults.filter((r) => r.status === "completed").length; const failedStages = stageResults.filter((r) => r.status === "failed").length; const overallStatus = failedStages > 0 ? "failed" : "completed"; // Update pipeline status pipeline.status = overallStatus; pipeline.updatedAt = new Date(); pipelineStore.set(pipeline.id, pipeline); return { artifacts, completedStages, duration: Date.now() - startTime, failedStages, pipelineId: pipeline.id, stageResults, status: overallStatus, totalStages, }; } catch (error) { pipeline.status = "failed"; pipeline.updatedAt = new Date(); pipelineStore.set(pipeline.id, pipeline); throw new Error(`Pipeline execution failed: ${error instanceof Error ? error.message : "Unknown error"}`); } }, executeStage: async (stage, pipeline, signer) => { const startTime = Date.now(); try { // Update stage status stage.status = "running"; stage.startTime = new Date(); let result; let artifacts = []; switch (stage.name) { case "deploy": result = await executeDeployStage(stage, pipeline, deployService, signer); artifacts = createDeploymentArtifacts(result); break; case "develop": result = await executeDevelopStage(stage, pipeline, tealCompilerService); artifacts = createDevelopmentArtifacts(result); break; case "docs": result = await executeDocsStage(stage, pipeline, docsService); break; case "test": result = await executeTestStage(stage, pipeline, aoLiteTestService); artifacts = createTestArtifacts(result); break; default: throw new Error(`Unknown stage: ${stage.name}`); } // Update stage status based on results let stageStatus = "completed"; // Check for failures in different stage types if (stage.name === "develop" && result.compilation && !result.compilation.success) { stageStatus = "failed"; } else if (stage.name === "test" && result.testResults && result.testResults.status === "failed") { stageStatus = "failed"; } else if (stage.name === "deploy" && result.deployStatus && result.deployStatus === "failed") { stageStatus = "failed"; } stage.status = stageStatus; stage.endTime = new Date(); stage.duration = Date.now() - startTime; stage.results = result; return { artifacts, duration: Date.now() - startTime, name: stage.name, output: result, stageId: stage.id, status: stageStatus, }; } catch (error) { stage.status = "failed"; stage.endTime = new Date(); stage.duration = Date.now() - startTime; stage.error = error instanceof Error ? error.message : "Unknown error"; return { duration: Date.now() - startTime, error: error instanceof Error ? error.message : "Unknown error", name: stage.name, stageId: stage.id, status: "failed", }; } }, generatePipelineReport: async (pipeline, results) => { try { const report = `# AO Development Pipeline Report ## Pipeline Information - **Name**: ${pipeline.name} - **ID**: ${pipeline.id} - **Status**: ${results.status} - **Duration**: ${results.duration}ms - **Created**: ${pipeline.createdAt.toISOString()} - **Updated**: ${pipeline.updatedAt.toISOString()} ## Execution Summary - **Total Stages**: ${results.totalStages} - **Completed**: ${results.completedStages} - **Failed**: ${results.failedStages} - **Success Rate**: ${((results.completedStages / results.totalStages) * 100).toFixed(1)}% ## Stage Results ${results.stageResults .map((stage) => ` ### ${stage.name} (${stage.status}) - **Duration**: ${stage.duration}ms ${stage.error ? `- **Error**: ${stage.error}` : ""} ${stage.output ? `- **Output**: ${JSON.stringify(stage.output, null, 2)}` : ""} `) .join("\n")} ## Artifacts Generated ${results.artifacts .map((artifact) => `\n### ${artifact.name} - **Type**: ${artifact.type} - **Size**: ${artifact.size} bytes - **Checksum**: ${artifact.checksum || "N/A"} `) .join("\n")} ## Configuration - **Auto Advance**: ${pipeline.configuration.autoAdvance || false} - **Stop on Error**: ${pipeline.configuration.stopOnError || false} - **Parallel Execution**: ${pipeline.configuration.parallelExecution || false} - **Timeout**: ${pipeline.configuration.timeout || "Default"} ## Metadata - **Author**: ${pipeline.metadata.author} - **Version**: ${pipeline.metadata.version} - **AO Version**: ${pipeline.metadata.aoVersion} - **Tags**: ${pipeline.metadata.tags.join(", ")} `; return report; } catch (error) { throw new Error(`Failed to generate pipeline report: ${error instanceof Error ? error.message : "Unknown error"}`); } }, getPipelineStatus: async (pipelineId) => { const pipeline = pipelineStore.get(pipelineId); if (!pipeline) { throw new Error(`Pipeline not found: ${pipelineId}`); } return pipeline; }, pausePipeline: async (pipelineId) => { const pipeline = pipelineStore.get(pipelineId); if (!pipeline) { throw new Error(`Pipeline not found: ${pipelineId}`); } pipeline.status = "draft"; // Paused state pipeline.updatedAt = new Date(); pipelineStore.set(pipelineId, pipeline); }, resumePipeline: async (pipelineId) => { const pipeline = pipelineStore.get(pipelineId); if (!pipeline) { throw new Error(`Pipeline not found: ${pipelineId}`); } pipeline.status = "running"; pipeline.updatedAt = new Date(); pipelineStore.set(pipelineId, pipeline); }, validateStageTransition: async (fromStage, toStage, pipeline) => { try { // Define valid transitions const validTransitions = { deploy: [], develop: ["test"], docs: ["develop"], test: ["deploy"], }; const validNext = validTransitions[fromStage] || []; if (!validNext.includes(toStage)) { return false; } // Check if previous stage completed successfully const fromStageObj = pipeline.stages.find((s) => s.name === fromStage); if (!fromStageObj || fromStageObj.status !== "completed") { return false; } return true; } catch { return false; } }, }; }; // Helper functions const generatePipelineId = () => { return `pipeline-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; }; const generateProcessId = () => { return `process-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; }; const createDefaultStages = async (processDefinition) => { return [ { configuration: { includeExamples: true, processType: "teal", queryPatterns: ["teal", "ao", "development"], }, id: "docs-stage", name: "docs", service: "AODevelopmentDocsService", status: "pending", }, { configuration: { compileOptions: processDefinition.metadata.compileOptions, generateDocs: true, validateTypes: true, }, id: "develop-stage", name: "develop", service: "TealCompilerService", status: "pending", }, { configuration: { concurrent: false, coverage: true, testSuite: "comprehensive", }, id: "test-stage", name: "test", service: "AOLiteTestService", status: "pending", }, { configuration: { monitor: true, network: "testnet", validate: true, }, id: "deploy-stage", name: "deploy", service: "PermawebDeployService", status: "pending", }, ]; }; const executeDocsStage = async (stage, pipeline, docsService) => { const query = `AO development ${pipeline.metadata.processType} best practices`; const context = { experience: "intermediate", framework: "teal", processType: pipeline.metadata.processType, stage: "develop", }; const docsResult = await docsService.queryAODevelopmentDocs(query, context); return { bestPractices: docsResult.bestPractices, examples: docsResult.codeExamples, guidance: docsResult.primaryGuidance, }; }; const executeDevelopStage = async (stage, pipeline, tealCompilerService) => { // This would compile the Teal source and validate it const compileResult = await tealCompilerService.compileTealToLua("-- Placeholder Teal source", stage.configuration .compileOptions); return { compilation: compileResult, typeChecks: compileResult.typeChecks, warnings: compileResult.warnings, }; }; const executeTestStage = async (stage, pipeline, aoLiteTestService) => { // Create a mock process definition for testing const mockProcessDefinition = { compiledLua: "-- Test compiled lua", dependencies: [], id: "test-process", metadata: { aoVersion: "2.0.0", author: "Test", compileOptions: {}, description: "Test process", version: "1.0.0", }, name: "Test Process", source: "-- Test source", typeDefinitions: [], version: "1.0.0", }; const testSuite = await aoLiteTestService.createDefaultTestSuite(mockProcessDefinition); const environment = await aoLiteTestService.createTestEnvironment(mockProcessDefinition); const testResults = await aoLiteTestService.executeTestSuite(testSuite, environment); return { coverage: testResults.coverage, testResults, }; }; const executeDeployStage = async (stage, _pipeline, _deployService, _signer) => { // This would deploy the compiled process return { deployment: { network: stage.configuration.network, processId: "deployed-process-id", status: "deployed", transactionId: "deploy-tx-id", }, }; }; const createDevelopmentArtifacts = (result) => { const artifacts = []; if (result.compilation?.compiledLua) { artifacts.push({ content: result.compilation.compiledLua, id: "compiled-lua", metadata: { compiler: "teal", warnings: result.warnings || [], }, name: "Compiled Lua", size: result.compilation.compiledLua.length, type: "compiled", }); } return artifacts; }; const createTestArtifacts = (result) => { const artifacts = []; if (result.testResults) { artifacts.push({ content: JSON.stringify(result.testResults, null, 2), id: "test-results", metadata: { coverage: result.coverage, passedTests: result.testResults.passedTests, totalTests: result.testResults.totalTests, }, name: "Test Results", size: JSON.stringify(result.testResults).length, type: "test", }); } return artifacts; }; const createDeploymentArtifacts = (result) => { const artifacts = []; if (result.deployment) { artifacts.push({ content: JSON.stringify(result.deployment, null, 2), id: "deployment-info", metadata: { network: result.deployment.network, processId: result.deployment.processId, transactionId: result.deployment.transactionId, }, name: "Deployment Information", size: JSON.stringify(result.deployment).length, type: "deployment", }); } return artifacts; }; const getQuickStartTemplate = (processType) => { const templates = { dao: `-- Quick Start DAO Process local record DAOState Name: string Proposals: {string} VotingPower: {string:number} end local State: DAOState = { Name = "Quick DAO", Proposals = {}, VotingPower = {} } local function info(msg: AO.Message): AO.Response return { Output = json.encode(State), Messages = {}, Spawns = {}, Assignments = {} } end Handlers.add("info", Handlers.utils.hasMatchingTag("Action", "Info"), info)`, game: `-- Quick Start Game Process local record GameState Name: string Players: {string:string} Status: string end local State: GameState = { Name = "Quick Game", Players = {}, Status = "Waiting" } local function info(msg: AO.Message): AO.Response return { Output = json.encode(State), Messages = {}, Spawns = {}, Assignments = {} } end Handlers.add("info", Handlers.utils.hasMatchingTag("Action", "Info"), info)`, generic: `-- Quick Start Generic Process local record ProcessState Name: string Data: {string:any} end local State: ProcessState = { Name = "Quick Process", Data = {} } local function info(msg: AO.Message): AO.Response return { Output = json.encode(State), Messages = {}, Spawns = {}, Assignments = {} } end Handlers.add("info", Handlers.utils.hasMatchingTag("Action", "Info"), info)`, token: `-- Quick Start Token Process local record TokenState Name: string Ticker: string TotalSupply: number Balances: {string:number} end local State: TokenState = { Name = "Quick Token", Ticker = "QT", TotalSupply = 1000000, Balances = {} } local function info(msg: AO.Message): AO.Response return { Output = json.encode(State), Messages = {}, Spawns = {}, Assignments = {} } end Handlers.add("info", Handlers.utils.hasMatchingTag("Action", "Info"), info)`, }; return templates[processType]; }; export const createAODevelopmentPipelineService = (docsService, tealCompilerService, aoLiteTestService, deployService, tealWorkflowService) => service(docsService, tealCompilerService, aoLiteTestService, deployService, tealWorkflowService);