Skip to content

Commit a83ea7e

Browse files
authored
Merge pull request #75 from SentriusLLC/copilot/fix-74
Add comprehensive endpoint scanning and capabilities API system
2 parents b6f1de6 + 2d263c5 commit a83ea7e

File tree

38 files changed

+1372
-72
lines changed

38 files changed

+1372
-72
lines changed

.local.env

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
SENTRIUS_VERSION=1.1.178
2-
SENTRIUS_SSH_VERSION=1.1.33
3-
SENTRIUS_KEYCLOAK_VERSION=1.1.46
4-
SENTRIUS_AGENT_VERSION=1.1.33
5-
SENTRIUS_AI_AGENT_VERSION=1.1.60
6-
LLMPROXY_VERSION=1.0.43
7-
LAUNCHER_VERSION=1.0.50
8-
AGENTPROXY_VERSION=1.0.63
1+
SENTRIUS_VERSION=1.1.193
2+
SENTRIUS_SSH_VERSION=1.1.35
3+
SENTRIUS_KEYCLOAK_VERSION=1.1.47
4+
SENTRIUS_AGENT_VERSION=1.1.34
5+
SENTRIUS_AI_AGENT_VERSION=1.1.64
6+
LLMPROXY_VERSION=1.0.46
7+
LAUNCHER_VERSION=1.0.51
8+
AGENTPROXY_VERSION=1.0.66

.local.env.bak

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
SENTRIUS_VERSION=1.1.178
2-
SENTRIUS_SSH_VERSION=1.1.33
3-
SENTRIUS_KEYCLOAK_VERSION=1.1.46
4-
SENTRIUS_AGENT_VERSION=1.1.33
5-
SENTRIUS_AI_AGENT_VERSION=1.1.60
6-
LLMPROXY_VERSION=1.0.43
7-
LAUNCHER_VERSION=1.0.50
8-
AGENTPROXY_VERSION=1.0.63
1+
SENTRIUS_VERSION=1.1.193
2+
SENTRIUS_SSH_VERSION=1.1.35
3+
SENTRIUS_KEYCLOAK_VERSION=1.1.47
4+
SENTRIUS_AGENT_VERSION=1.1.34
5+
SENTRIUS_AI_AGENT_VERSION=1.1.64
6+
LLMPROXY_VERSION=1.0.46
7+
LAUNCHER_VERSION=1.0.51
8+
AGENTPROXY_VERSION=1.0.66

ai-agent/src/main/java/io/sentrius/agent/analysis/agents/agents/AgentVerb.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.sentrius.agent.analysis.agents.agents;
22

33
import java.lang.reflect.Method;
4+
import java.util.List;
5+
import io.sentrius.sso.core.dto.capabilities.ParameterDescriptor;
46
import io.sentrius.sso.core.model.verbs.DefaultInterpreter;
57
import io.sentrius.sso.core.model.verbs.InputInterpreterIfc;
68
import io.sentrius.sso.core.model.verbs.OutputInterpreterIfc;
@@ -13,6 +15,8 @@ public class AgentVerb {
1315
private String name;
1416
private String description;
1517
private Method method;
18+
private List<ParameterDescriptor> paramDescriptions;
19+
private boolean isAiCallable = true;
1620
@Builder.Default
1721
private boolean requiresTokenManagement = false;
1822
@Builder.Default

ai-agent/src/main/java/io/sentrius/agent/analysis/agents/agents/ChatAgent.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
7979

8080
verbRegistry.scanClasspath();
8181

82+
8283
var keyPair = agentKeyService.getKeyPair();
8384

8485
try {

ai-agent/src/main/java/io/sentrius/agent/analysis/agents/agents/RegisteredAgent.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import java.util.HashMap;
44
import java.util.Map;
5+
import java.util.UUID;
56
import java.util.concurrent.TimeUnit;
67
import com.fasterxml.jackson.databind.node.ArrayNode;
78
import io.sentrius.agent.analysis.agents.verbs.AgentVerbs;
9+
import io.sentrius.agent.analysis.api.AgentKeyService;
10+
import io.sentrius.agent.config.AgentConfigOptions;
811
import io.sentrius.sso.core.dto.ztat.AgentExecution;
912
import io.sentrius.sso.core.dto.ztat.ZtatRequestDTO;
1013
import io.sentrius.sso.core.exceptions.ZtatException;
@@ -14,6 +17,7 @@
1417
import io.sentrius.sso.core.services.agents.AgentExecutionService;
1518
import io.sentrius.sso.core.services.agents.ZeroTrustClientService;
1619
import io.sentrius.sso.core.dto.UserDTO;
20+
import io.sentrius.sso.core.services.security.KeycloakService;
1721
import io.sentrius.sso.core.utils.JsonUtil;
1822
import jakarta.annotation.PreDestroy;
1923
import lombok.RequiredArgsConstructor;
@@ -35,6 +39,9 @@ public class RegisteredAgent implements ApplicationListener<ApplicationReadyEven
3539
final VerbRegistry verbRegistry;
3640
final AgentVerbs agentVerbs;
3741
final AgentExecutionService agentExecutionService;
42+
final AgentConfigOptions agentConfigOptions;
43+
final AgentKeyService agentKeyService;
44+
private final KeycloakService keycloakService;
3845

3946
private volatile boolean running = true;
4047
private Thread workerThread;
@@ -70,10 +77,12 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
7077

7178
verbRegistry.scanClasspath();
7279

73-
final UserDTO user = UserDTO.builder()
80+
UserDTO user = UserDTO.builder()
7481
.username(zeroTrustClientService.getUsername())
7582
.build();
7683
var execution = agentExecutionService.getAgentExecution(user);
84+
85+
var keyPair = agentKeyService.getKeyPair();
7786
try {
7887
agentClientService.heartbeat(execution, execution.getUser().getUsername());
7988
} catch (ZtatException e) {
@@ -101,15 +110,16 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
101110
}
102111
}
103112

113+
UserDTO finalUser = user;
104114
workerThread = new Thread(() -> {
105115
try {
106116

107-
log.info("Username: {}", user.getUsername());
117+
log.info("Username: {}", finalUser.getUsername());
108118
log.info("Registering v1.0.2 agent...");
109119

110120

111121

112-
var agentExecution = agentExecutionService.getAgentExecution(user);
122+
var agentExecution = agentExecutionService.getAgentExecution(finalUser);
113123
var response = promptAgent(agentExecution);
114124
while (running) {
115125
try {

ai-agent/src/main/java/io/sentrius/agent/analysis/agents/agents/VerbRegistry.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
import io.github.classgraph.ClassGraph;
44
import io.github.classgraph.ScanResult;
5+
import io.sentrius.sso.core.dto.capabilities.EndpointDescriptor;
6+
import io.sentrius.agent.discovery.AgentEndpointDiscoveryService;
57
import io.sentrius.sso.core.dto.ztat.AgentExecution;
68
import io.sentrius.sso.core.dto.ztat.ZtatRequestDTO;
79
import io.sentrius.sso.core.exceptions.ZtatException;
810
import io.sentrius.sso.core.model.verbs.OutputInterpreterIfc;
911
import io.sentrius.sso.core.model.verbs.Verb;
1012
import io.sentrius.sso.core.model.verbs.VerbResponse;
1113
import io.sentrius.sso.core.services.agents.ZeroTrustClientService;
14+
import io.sentrius.sso.core.services.capabilities.EndpointScanningService;
1215
import lombok.RequiredArgsConstructor;
1316
import lombok.extern.slf4j.Slf4j;
1417
import org.springframework.context.ApplicationContext;
@@ -18,8 +21,10 @@
1821
import java.lang.reflect.InvocationTargetException;
1922
import java.lang.reflect.Method;
2023
import java.util.HashMap;
24+
import java.util.List;
2125
import java.util.Map;
2226
import java.util.concurrent.TimeUnit;
27+
import java.util.stream.Collectors;
2328

2429
@Slf4j
2530
@Component
@@ -29,10 +34,14 @@ public class VerbRegistry {
2934
private final ApplicationContext applicationContext;
3035

3136
private final ZeroTrustClientService zeroTrustClientService;
37+
38+
private final EndpointScanningService endpointScanningService;
3239

3340
private final Map<String, AgentVerb> verbs = new HashMap<>();
3441
private final Map<String, Object> instances = new HashMap<>();
3542

43+
private final AgentEndpointDiscoveryService agentEndpointDiscoveryService;
44+
3645
public void scanClasspath() {
3746
// Scan the classpath for classes with the @Verb annotation
3847
synchronized (this) {
@@ -167,4 +176,30 @@ public VerbResponse execute(AgentExecution agentExecution, VerbResponse priorRes
167176
public Map<String, AgentVerb> getVerbs() {
168177
return new HashMap<>(verbs);
169178
}
179+
180+
/**
181+
* Gets endpoint descriptors for all registered verbs.
182+
* This provides integration with the centralized endpoint scanning system.
183+
*/
184+
public List<EndpointDescriptor> getVerbDescriptors() {
185+
return endpointScanningService.getAllEndpoints()
186+
.stream()
187+
.filter(endpoint -> "VERB".equals(endpoint.getType()))
188+
.filter(endpoint -> verbs.containsKey(endpoint.getName()))
189+
.collect(Collectors.toList());
190+
}
191+
192+
/**
193+
* Gets all available AI-callable verb descriptors.
194+
* This can be used by agents to understand what capabilities are available.
195+
*/
196+
public List<EndpointDescriptor> getAiCallableVerbDescriptors() {
197+
return getVerbDescriptors()
198+
.stream()
199+
.filter(endpoint -> {
200+
Boolean isAiCallable = (Boolean) endpoint.getMetadata().get("isAiCallable");
201+
return isAiCallable != null && isAiCallable;
202+
})
203+
.collect(Collectors.toList());
204+
}
170205
}

ai-agent/src/main/java/io/sentrius/agent/config/AgentConfigOptions.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.sentrius.agent.config;
22

3+
import java.util.List;
34
import lombok.Getter;
45
import lombok.Setter;
56
import lombok.extern.slf4j.Slf4j;
@@ -14,4 +15,5 @@ public class AgentConfigOptions {
1415

1516
private String namePrefix;
1617
private String type;
18+
private List<String> endpoints;
1719
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package io.sentrius.agent.discovery;
2+
3+
import io.sentrius.agent.analysis.agents.agents.AgentVerb;
4+
import io.sentrius.agent.config.AgentConfigOptions;
5+
import io.sentrius.sso.core.dto.capabilities.EndpointDescriptor;
6+
import io.sentrius.sso.core.dto.ztat.AgentExecution;
7+
import io.sentrius.sso.core.exceptions.ZtatException;
8+
import io.sentrius.sso.core.model.verbs.DefaultInterpreter;
9+
import io.sentrius.sso.core.services.agents.ZeroTrustClientService;
10+
import lombok.RequiredArgsConstructor;
11+
import lombok.extern.slf4j.Slf4j;
12+
import org.springframework.http.ResponseEntity;
13+
import org.springframework.stereotype.Service;
14+
import org.springframework.web.client.RestTemplate;
15+
16+
import java.util.*;
17+
import java.util.stream.Collectors;
18+
19+
@Slf4j
20+
@Service
21+
@RequiredArgsConstructor
22+
public class AgentEndpointDiscoveryService {
23+
24+
private final AgentConfigOptions agentConfigOptions;
25+
private final RestTemplate restTemplate = new RestTemplate();
26+
private final ZeroTrustClientService zeroTrustClientService;
27+
28+
// Simulated verb registry for example
29+
private final Map<String, AgentVerb> verbs = new HashMap<>();
30+
31+
public Map<String, AgentVerb> discoverEndpoints(AgentExecution agentExecution) {
32+
String discoveryUrl = agentConfigOptions.getEndpoints() + "?type=VERB";
33+
34+
log.info("Querying discovery endpoint: {}", discoveryUrl);
35+
36+
try {
37+
38+
List<EndpointDescriptor> descriptors = zeroTrustClientService.callGetOnApi(agentExecution, discoveryUrl);
39+
if (descriptors == null || descriptors.isEmpty()) {
40+
log.info("No endpoints discovered from capabilities API");
41+
return verbs;
42+
}
43+
44+
List<EndpointDescriptor> verbEndpoints = descriptors.stream()
45+
.filter(d -> "VERB".equalsIgnoreCase(d.getType()))
46+
.toList();
47+
48+
log.info("Discovered {} VERB endpoints", verbEndpoints.size());
49+
50+
for (EndpointDescriptor endpoint : verbEndpoints) {
51+
if (!verbs.containsKey(endpoint.getName())) {
52+
log.warn("Discovered verb '{}' not registered in agent", endpoint.getName());
53+
// You could also register it here dynamically if desired
54+
verbs.put(endpoint.getName(), convertToVerbDefinition(endpoint));
55+
}
56+
}
57+
58+
} catch (Exception e) {
59+
log.error("Failed to discover endpoints: {}", e.getMessage(), e);
60+
} catch (ZtatException e) {
61+
throw new RuntimeException(e);
62+
}
63+
return verbs;
64+
}
65+
66+
private AgentVerb convertToVerbDefinition(EndpointDescriptor descriptor) {
67+
return AgentVerb.builder()
68+
.name(descriptor.getName())
69+
.description(descriptor.getDescription())
70+
.returnType(descriptor.getReturnType() != null ? descriptor.getReturnType() : String.class)
71+
.requiresTokenManagement(descriptor.isRequiresTokenManagement())
72+
.outputInterpreter(DefaultInterpreter.class) // You can enhance this later if `metadata` has interpreter info
73+
.inputInterpreter(DefaultInterpreter.class)
74+
.paramDescriptions(descriptor.getParameters())
75+
.isAiCallable(true) // Assume everything from the API is callable
76+
.build();
77+
}
78+
}

api/src/main/java/io/sentrius/sso/config/AppConfig.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,13 @@ public class AppConfig {
1212
// Your configuration beans
1313
@Value("${agentproxy.externalUrl:}") // Defaults to empty string if not set
1414
private String agentProxyExternalUrl;
15+
16+
@Value("${sentrius.agent.register.bootstrap.allow:false}")
17+
private boolean allowRegistration;
18+
19+
@Value("${sentrius.agent.bootstrap.policy:default-policy.yaml}")
20+
private String defaultPolicyFile;
21+
22+
@Value("${sentrius.agent.launcher.service:http://sentrius-launcherservice:8080/}")
23+
private String sentriusLauncherService;
1524
}

api/src/main/java/io/sentrius/sso/controllers/api/AgentApiController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public ResponseEntity<?> requestRegistration(
151151
@RequestHeader("Authorization") String token,
152152
HttpServletRequest request, HttpServletResponse response) throws SQLException, GeneralSecurityException {
153153

154-
String compactJwt = token.startsWith("Bearer ") ? token.substring(7) : token;
154+
String compactJwt = token.startsWith("Beaer ") ? token.substring(7) : token;
155155

156156

157157
if (!keycloakService.validateJwt(compactJwt)) {

0 commit comments

Comments
 (0)