Coverage for src/alprina_cli/acp_server.py: 5%

77 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2025-11-14 11:27 +0100

1""" 

2ACP (Agent Client Protocol) server for IDE integration. 

3Allows Alprina to be used as an IDE agent. 

4""" 

5 

6from typing import Any, Dict 

7from rich.console import Console 

8 

9console = Console() 

10 

11 

12def run_acp(): 

13 """ 

14 Start Alprina in ACP mode for IDE integration. 

15 """ 

16 console.print("[cyan]Starting ACP server...[/cyan]") 

17 

18 try: 

19 from agent_client_protocol import ACPServer, models 

20 

21 class AlprinaACPServer(ACPServer): 

22 """Alprina ACP Server implementation.""" 

23 

24 async def handle_request(self, req: models.Request) -> models.Response: 

25 """Handle incoming ACP requests from IDE.""" 

26 method = req.method 

27 params = req.params or {} 

28 

29 console.print(f"[dim]ACP Request: {method}[/dim]") 

30 

31 try: 

32 if method == "scan": 

33 result = await self._handle_scan(params) 

34 return models.Response.ok(req.id, result) 

35 

36 elif method == "recon": 

37 result = await self._handle_recon(params) 

38 return models.Response.ok(req.id, result) 

39 

40 elif method == "mitigate": 

41 result = await self._handle_mitigate(params) 

42 return models.Response.ok(req.id, result) 

43 

44 elif method == "analyze_file": 

45 result = await self._handle_analyze_file(params) 

46 return models.Response.ok(req.id, result) 

47 

48 else: 

49 return models.Response.error( 

50 req.id, 

51 -32601, 

52 f"Method not found: {method}" 

53 ) 

54 

55 except Exception as e: 

56 console.print(f"[red]Error handling request: {e}[/red]") 

57 return models.Response.error( 

58 req.id, 

59 -32603, 

60 f"Internal error: {str(e)}" 

61 ) 

62 

63 async def _handle_scan(self, params: Dict[str, Any]) -> Dict[str, Any]: 

64 """Handle scan request.""" 

65 target = params.get("target") 

66 profile = params.get("profile", "default") 

67 safe_only = params.get("safe_only", True) 

68 

69 if not target: 

70 raise ValueError("Target parameter is required") 

71 

72 from .security_engine import run_local_scan, run_remote_scan 

73 from pathlib import Path 

74 

75 # Determine if local or remote 

76 target_path = Path(target) 

77 if target_path.exists(): 

78 result = run_local_scan(target, profile, safe_only) 

79 else: 

80 result = run_remote_scan(target, profile, safe_only) 

81 

82 return result 

83 

84 async def _handle_recon(self, params: Dict[str, Any]) -> Dict[str, Any]: 

85 """Handle recon request.""" 

86 target = params.get("target") 

87 passive = params.get("passive", True) 

88 

89 if not target: 

90 raise ValueError("Target parameter is required") 

91 

92 from .security_engine import run_agent 

93 

94 result = run_agent( 

95 task="web-recon", 

96 input_data=target, 

97 metadata={"passive": passive} 

98 ) 

99 

100 return result 

101 

102 async def _handle_mitigate(self, params: Dict[str, Any]) -> Dict[str, Any]: 

103 """Handle mitigation request.""" 

104 finding = params.get("finding") 

105 

106 if not finding: 

107 raise ValueError("Finding parameter is required") 

108 

109 from .security_engine import run_agent 

110 

111 mitigation_prompt = f""" 

112 Security Finding: {finding.get('description')} 

113 Type: {finding.get('type')} 

114 Severity: {finding.get('severity')} 

115 

116 Provide step-by-step mitigation guidance. 

117 """ 

118 

119 result = run_agent( 

120 task="mitigation", 

121 input_data=mitigation_prompt, 

122 metadata=finding 

123 ) 

124 

125 return result 

126 

127 async def _handle_analyze_file(self, params: Dict[str, Any]) -> Dict[str, Any]: 

128 """Handle file analysis request.""" 

129 file_path = params.get("file_path") 

130 content = params.get("content") 

131 

132 if not file_path and not content: 

133 raise ValueError("Either file_path or content is required") 

134 

135 from pathlib import Path 

136 from .security_engine import run_agent 

137 

138 if file_path: 

139 content = Path(file_path).read_text() 

140 

141 result = run_agent( 

142 task="code-audit", 

143 input_data=content, 

144 metadata={"file": file_path} 

145 ) 

146 

147 return result 

148 

149 # Start the ACP server with stdio transport 

150 server = AlprinaACPServer(transport="stdio") 

151 console.print("[green]ACP server started on stdio[/green]") 

152 server.serve() 

153 

154 except ImportError: 

155 console.print("[red]agent-client-protocol not installed[/red]") 

156 console.print("[yellow]Install with: pip install agent-client-protocol[/yellow]") 

157 except Exception as e: 

158 console.print(f"[red]Failed to start ACP server: {e}[/red]")