From abdad25df3898aa63a3fa7133fcf1840314dede7 Mon Sep 17 00:00:00 2001 From: dongmucat <1127093059@qq.com> Date: Mon, 15 Jun 2026 14:45:09 +0800 Subject: [PATCH 1/2] feat(bootstrap): add agentguard builtin skill Signed-off-by: dongmucat <1127093059@qq.com> --- .../resources/builtin-skills/manifest.json | 5 +++ .../skill/metadata/SkillMetadataParser.java | 12 ++++++ .../metadata/SkillMetadataParserTest.java | 40 +++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/server/skillhub-app/src/main/resources/builtin-skills/manifest.json b/server/skillhub-app/src/main/resources/builtin-skills/manifest.json index 8dba7bac..a9fc8cac 100644 --- a/server/skillhub-app/src/main/resources/builtin-skills/manifest.json +++ b/server/skillhub-app/src/main/resources/builtin-skills/manifest.json @@ -4,6 +4,11 @@ "slug": "skillhub-hello", "version": "1.0.0", "url": "https://bjcdn.openstorage.cn/aicontest/2026-06-11/f8a59af3-30d4-4031-80f6-ebff74b05195.zip" + }, + { + "slug": "agentguard", + "version": "1.1", + "url": "https://bjcdn.openstorage.cn/aicontest/2026-06-12/9d063bc7-223a-4762-adeb-305c268aa29e.zip" } ] } diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/metadata/SkillMetadataParser.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/metadata/SkillMetadataParser.java index 36fc1ffc..c44a4516 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/metadata/SkillMetadataParser.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/metadata/SkillMetadataParser.java @@ -50,6 +50,9 @@ public class SkillMetadataParser { String name = extractRequiredField(frontmatter, "name"); String description = extractRequiredField(frontmatter, "description"); String version = extractOptionalField(frontmatter, "version"); + if (version == null) { + version = extractNestedOptionalField(frontmatter, "metadata", "version"); + } return new SkillMetadata(name, description, version, body, frontmatter); } @@ -122,4 +125,13 @@ public class SkillMetadataParser { Object value = frontmatter.get(fieldName); return value == null ? null : value.toString(); } + + private String extractNestedOptionalField(Map frontmatter, String objectFieldName, String fieldName) { + Object nestedValue = frontmatter.get(objectFieldName); + if (!(nestedValue instanceof Map nestedMap)) { + return null; + } + Object value = nestedMap.get(fieldName); + return value == null ? null : value.toString(); + } } diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/metadata/SkillMetadataParserTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/metadata/SkillMetadataParserTest.java index 15969023..aececcd2 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/metadata/SkillMetadataParserTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/metadata/SkillMetadataParserTest.java @@ -123,6 +123,46 @@ class SkillMetadataParserTest { assertNull(metadata.version()); } + @Test + void testUsesMetadataVersionWhenTopLevelVersionIsMissing() { + String content = """ + --- + name: agentguard + description: Agent security guard + metadata: + author: GoPlusSecurity + version: "1.1" + --- + Body + """; + + SkillMetadata metadata = parser.parse(content); + + assertEquals("agentguard", metadata.name()); + assertEquals("Agent security guard", metadata.description()); + assertEquals("1.1", metadata.version()); + } + + @Test + void testTopLevelVersionTakesPrecedenceOverMetadataVersion() { + String content = """ + --- + name: versioned-skill + description: Prefer top-level version + version: 2.0.0 + metadata: + version: "1.1" + --- + Body + """; + + SkillMetadata metadata = parser.parse(content); + + assertEquals("versioned-skill", metadata.name()); + assertEquals("Prefer top-level version", metadata.description()); + assertEquals("2.0.0", metadata.version()); + } + @Test void testFallsBackToLooseFrontmatterParsingWhenYamlSyntaxIsNotStrict() { String content = """ From 6d6577faa27f9eae586aa4bb45ff6ef5e491c4b9 Mon Sep 17 00:00:00 2001 From: dongmucat <1127093059@qq.com> Date: Tue, 16 Jun 2026 10:23:02 +0800 Subject: [PATCH 2/2] fix(bootstrap): confirm builtin publish warnings Signed-off-by: dongmucat <1127093059@qq.com> --- .../bootstrap/BuiltinSkillInitializer.java | 3 +- .../BuiltinSkillInitializerTest.java | 31 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/bootstrap/BuiltinSkillInitializer.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/bootstrap/BuiltinSkillInitializer.java index 20b9a363..7ed8c00d 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/bootstrap/BuiltinSkillInitializer.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/bootstrap/BuiltinSkillInitializer.java @@ -50,6 +50,7 @@ public class BuiltinSkillInitializer { private static final Logger log = LoggerFactory.getLogger(BuiltinSkillInitializer.class); private static final Set SYSTEM_PUBLISHER_ROLES = Set.of("SUPER_ADMIN"); + private static final boolean CONFIRM_BUILTIN_PUBLISH_WARNINGS = true; private final BuiltinSkillProperties properties; private final BuiltinSkillManifestLoader manifestLoader; @@ -243,7 +244,7 @@ public class BuiltinSkillInitializer { SYSTEM_PUBLISHER_ID, SkillVisibility.PUBLIC, SYSTEM_PUBLISHER_ROLES, - false + CONFIRM_BUILTIN_PUBLISH_WARNINGS ); log.info("Published built-in skill slug={} version={} to @{}", item.slug(), item.version(), GLOBAL_NAMESPACE); diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/bootstrap/BuiltinSkillInitializerTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/bootstrap/BuiltinSkillInitializerTest.java index 296f547a..bc5602a0 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/bootstrap/BuiltinSkillInitializerTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/bootstrap/BuiltinSkillInitializerTest.java @@ -2,6 +2,7 @@ package com.iflytek.skillhub.bootstrap; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.never; @@ -106,7 +107,7 @@ class BuiltinSkillInitializerTest { runInitializer(); verify(manifestLoader, never()).load(); - verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), eq(false)); + verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), anyBoolean()); } @Test @@ -116,7 +117,7 @@ class BuiltinSkillInitializerTest { runInitializer(); verify(manifestLoader, never()).load(); - verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), eq(false)); + verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), anyBoolean()); } @Test @@ -144,7 +145,7 @@ class BuiltinSkillInitializerTest { verify(namespaceMemberRepository, never()).save(any()); verify(downloader, never()).download(any()); - verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), eq(false)); + verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), anyBoolean()); } @Test @@ -162,7 +163,7 @@ class BuiltinSkillInitializerTest { runInitializer(); verify(downloader, never()).download(any()); - verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), eq(false)); + verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), anyBoolean()); } @Test @@ -180,7 +181,7 @@ class BuiltinSkillInitializerTest { runInitializer(); verify(downloader, never()).download(any()); - verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), eq(false)); + verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), anyBoolean()); } @Test @@ -192,7 +193,7 @@ class BuiltinSkillInitializerTest { runInitializer(); verify(downloader, never()).download(any()); - verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), eq(false)); + verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), anyBoolean()); } @Test @@ -206,7 +207,7 @@ class BuiltinSkillInitializerTest { runInitializer(); verify(downloader).download(URI.create(ITEM.url())); - verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), eq(false)); + verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), anyBoolean()); } @Test @@ -222,7 +223,7 @@ class BuiltinSkillInitializerTest { runInitializer(); verify(downloader, never()).download(any()); - verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), eq(false)); + verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), anyBoolean()); assertThat(output).doesNotContain("Failed to synchronize built-in skill slug=skillhub-hello"); } @@ -232,7 +233,7 @@ class BuiltinSkillInitializerTest { runInitializer(); - verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), eq(false)); + verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), anyBoolean()); } @Test @@ -241,7 +242,7 @@ class BuiltinSkillInitializerTest { runInitializer(); - verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), eq(false)); + verify(skillPublishService, never()).publishFromEntries(any(), any(), any(), any(), any(), anyBoolean()); } @Test @@ -259,7 +260,7 @@ class BuiltinSkillInitializerTest { eq(PUBLISHER), eq(SkillVisibility.PUBLIC), eq(Set.of("SUPER_ADMIN")), - eq(false) + eq(true) ); assertThat(entriesCaptor.getValue()).isEqualTo(entries); } @@ -297,7 +298,7 @@ class BuiltinSkillInitializerTest { .thenReturn(List.of()) .thenReturn(List.of(builtinSkill)); when(skillPublishService.publishFromEntries( - eq(GLOBAL), any(), eq(PUBLISHER), eq(SkillVisibility.PUBLIC), eq(Set.of("SUPER_ADMIN")), eq(false))) + eq(GLOBAL), any(), eq(PUBLISHER), eq(SkillVisibility.PUBLIC), eq(Set.of("SUPER_ADMIN")), eq(true))) .thenThrow(new DomainBadRequestException("error.skill.version.exists", "1.0.0")); when(skillVersionRepository.findBySkillIdAndVersion(100L, "1.0.0")).thenReturn(Optional.of(published)); when(skillFileRepository.findByVersionId(200L)).thenReturn(skillFilesFor(entries, 200L)); @@ -305,7 +306,7 @@ class BuiltinSkillInitializerTest { runInitializer(); verify(skillPublishService).publishFromEntries( - eq(GLOBAL), any(), eq(PUBLISHER), eq(SkillVisibility.PUBLIC), eq(Set.of("SUPER_ADMIN")), eq(false)); + eq(GLOBAL), any(), eq(PUBLISHER), eq(SkillVisibility.PUBLIC), eq(Set.of("SUPER_ADMIN")), eq(true)); } @Test @@ -318,7 +319,7 @@ class BuiltinSkillInitializerTest { .thenReturn(List.of()) .thenReturn(List.of(builtinSkill)); when(skillPublishService.publishFromEntries( - eq(GLOBAL), any(), eq(PUBLISHER), eq(SkillVisibility.PUBLIC), eq(Set.of("SUPER_ADMIN")), eq(false))) + eq(GLOBAL), any(), eq(PUBLISHER), eq(SkillVisibility.PUBLIC), eq(Set.of("SUPER_ADMIN")), eq(true))) .thenThrow(new DomainBadRequestException("error.skill.version.exists", "1.0.0")); when(skillVersionRepository.findBySkillIdAndVersion(100L, "1.0.0")).thenReturn(Optional.of(published)); when(skillFileRepository.findByVersionId(200L)).thenReturn(List.of( @@ -329,7 +330,7 @@ class BuiltinSkillInitializerTest { verify(skillFileRepository).findByVersionId(200L); verify(skillPublishService).publishFromEntries( - eq(GLOBAL), any(), eq(PUBLISHER), eq(SkillVisibility.PUBLIC), eq(Set.of("SUPER_ADMIN")), eq(false)); + eq(GLOBAL), any(), eq(PUBLISHER), eq(SkillVisibility.PUBLIC), eq(Set.of("SUPER_ADMIN")), eq(true)); assertThat(output).contains("Failed to publish built-in skill slug=skillhub-hello version=1.0.0"); assertThat(output).doesNotContain("was published concurrently, skipping"); }