This is an automated email from the ASF dual-hosted git repository. luigidemasi pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-kit.git
commit d94ec8f4f95a5be0cb46be84b135ea270b8f2ea8 Author: Jinal Sarvaiya <[email protected]> AuthorDate: Fri Nov 6 07:43:53 2026 +0000 fix: ancestor/explicit config enumeration, native type-named server --- .../camel/kit/generator/OpenCodeGenerator.java | 47 +++++++++++++++++++- .../camel/kit/generator/OpenCodeGeneratorTest.java | 51 ++++++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java b/kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java index 3f3f2743..32943b3c 100644 --- a/kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java +++ b/kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java @@ -26,6 +26,10 @@ public class OpenCodeGenerator extends DefaultGenerator { private static final List<String> GLOBAL_CONFIG_FILES = List.of("opencode.json", "opencode.jsonc"); + private static final List<String> ANCESTOR_CONFIG_FILES = List.of( + "opencode.json", "opencode.jsonc", + ".opencode/opencode.json", ".opencode/opencode.jsonc"); + private final QuteTemplateEngine templateEngine = new QuteTemplateEngine(); @Override @@ -78,13 +82,20 @@ public class OpenCodeGenerator extends DefaultGenerator { for (Path configFile : OpenCodeProjectConfig.existingFiles(ctx.projectDir())) { collectMcpServersFromFile(configFile, names); } + for (Path ancestorFile : ancestorConfigFiles(ctx.projectDir())) { + collectMcpServersFromFile(ancestorFile, names); + } for (Path globalFile : globalConfigFiles()) { collectMcpServersFromFile(globalFile, names); } + Path explicitConfig = explicitConfigFile(); + if (explicitConfig != null) { + collectMcpServersFromFile(explicitConfig, names); + } return List.copyOf(names); } - private static void collectMcpServersFromFile(Path configFile, Set<String> names) throws IOException { + static void collectMcpServersFromFile(Path configFile, Set<String> names) throws IOException { JsonNode config = OpenCodeProjectConfig.newJsonMapper().readTree(configFile.toFile()); if (config == null) { return; @@ -94,7 +105,7 @@ public class OpenCodeGenerator extends DefaultGenerator { return; } JsonNode servers = mcp.path("servers"); - boolean hasNativeContainer = servers.isObject() && !servers.has("type"); + boolean hasNativeContainer = servers.isObject() && isNativeServersContainer(servers); if (hasNativeContainer) { servers.fields().forEachRemaining(entry -> { if (entry.getValue().isObject()) { @@ -110,6 +121,27 @@ public class OpenCodeGenerator extends DefaultGenerator { }); } + private static boolean isNativeServersContainer(JsonNode servers) { + JsonNode type = servers.path("type"); + return !type.isTextual(); + } + + static List<Path> ancestorConfigFiles(Path projectDir) throws IOException { + Path realProject = projectDir.toRealPath(); + Path parent = realProject.getParent(); + List<Path> result = new java.util.ArrayList<>(); + while (parent != null) { + for (String name : ANCESTOR_CONFIG_FILES) { + Path candidate = parent.resolve(name); + if (Files.exists(candidate)) { + result.add(candidate); + } + } + parent = parent.getParent(); + } + return result; + } + static List<Path> globalConfigFiles() { Path globalDir = globalConfigDir(); return GLOBAL_CONFIG_FILES.stream() @@ -126,6 +158,17 @@ public class OpenCodeGenerator extends DefaultGenerator { return Path.of(System.getProperty("user.home"), ".config", "opencode"); } + static Path explicitConfigFile() { + String explicitPath = System.getenv("OPENCODE_CONFIG"); + if (explicitPath != null && !explicitPath.isBlank()) { + Path path = Path.of(explicitPath); + if (Files.exists(path)) { + return path; + } + } + return null; + } + private void generateAgents(InitContext ctx, Map<String, Object> data, boolean includeShipWorker) throws Exception { Path agentsDir = ctx.projectDir().resolve(".opencode/agents"); diff --git a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/OpenCodeGeneratorTest.java b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/OpenCodeGeneratorTest.java index f5514d1c..b15fd00c 100644 --- a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/OpenCodeGeneratorTest.java +++ b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/OpenCodeGeneratorTest.java @@ -1156,4 +1156,55 @@ class OpenCodeGeneratorTest { assertEquals("__", OpenCodeGenerator.normalizeServerName("üñ"), "All non-ASCII characters must be replaced"); } + + @Test + void shipWorkerDeniesServersFromAncestorConfig() throws Exception { + InitContext ctx = createContext(); + + Path ancestorDir = tempDir.getParent(); + Path ancestorConfig = ancestorDir.resolve("opencode.json"); + Files.writeString(ancestorConfig, """ + { + "mcp": { + "ancestor-probe": {"type": "local", "command": ["echo"], "enabled": true} + } + } + """); + + try { + new OpenCodeGenerator().generate(ctx); + + String worker = Files.readString( + tempDir.resolve(".opencode/agents/camel-ship-worker.md")); + assertTrue(worker.contains("ancestor-probe_*: deny"), + "Ship worker must deny MCP servers inherited from ancestor config"); + } finally { + Files.deleteIfExists(ancestorConfig); + } + } + + @Test + void shipWorkerDeniesNativeFormatServerNamedType() throws Exception { + InitContext ctx = createContext(); + + Files.writeString(tempDir.resolve("opencode.json"), """ + { + "mcp": { + "servers": { + "type": {"type": "local", "command": ["echo"], "enabled": true}, + "probe": {"type": "local", "command": ["echo"], "enabled": true} + } + } + } + """); + + new OpenCodeGenerator().generate(ctx); + + String worker = Files.readString( + tempDir.resolve(".opencode/agents/camel-ship-worker.md")); + assertTrue(worker.contains("type_*: deny"), + "Ship worker must deny a native server named 'type'"); + assertTrue(worker.contains("probe_*: deny"), + "Ship worker must deny other servers in the same native container"); + } }
