feat(plugins): browse and install official plugins from catalog (#1088) (#1096)

This commit is contained in:
M57 authored and GitHub committed 2026-09-12 22:32:52 +03:00
1 parent 47b6955c9a
commit d774a63663
8 files changed
+840 -71

No files matched your search

@@ -80,9 +80,63 @@ data class PluginsHubResponse(
@Serializable
data class AgentPluginInstallBody(
val identifier: String,
val identifier: String = "",
val force: Boolean = false,
val enable: Boolean = true,
@SerialName("catalog_name") val catalogName: String? = null,
val ref: String? = null,
)
@Serializable
data class PluginCatalogCapabilities(
@SerialName("provides_tools") val providesTools: List<String> = emptyList(),
@SerialName("provides_hooks") val providesHooks: List<String> = emptyList(),
@SerialName("provides_middleware") val providesMiddleware: List<String> = emptyList(),
@SerialName("requires_env") val requiresEnv: List<String> = emptyList(),
)
@Serializable
data class PluginCatalogRemovedEntry(
val name: String? = null,
val repo: String? = null,
val reason: String? = null,
val date: String? = null,
)
@Serializable
data class PluginCatalogEntry(
val name: String,
@SerialName("catalog_name") val catalogName: String? = null,
val repo: String? = null,
val sha: String? = null,
@SerialName("sha_short") val shaShort: String? = null,
val description: String? = null,
val maintainer: String? = null,
val tier: String? = null,
val category: String? = null,
@SerialName("requires_hermes") val requiresHermes: String? = null,
val subdir: String? = null,
@SerialName("docs_url") val docsUrl: String? = null,
val platforms: List<String> = emptyList(),
val capabilities: PluginCatalogCapabilities? = null,
@SerialName("capability_summary") val capabilitySummary: String? = null,
val installed: Boolean = false,
@SerialName("installed_sha") val installedSha: String? = null,
@SerialName("update_available") val updateAvailable: Boolean = false,
@SerialName("runtime_status") val runtimeStatus: String? = null,
) {
val displayName: String
get() = catalogName?.takeIf { it.isNotBlank() } ?: name
val isOfficial: Boolean
get() = tier?.equals("official", ignoreCase = true) == true
}
@Serializable
data class PluginCatalogResponse(
val entries: List<PluginCatalogEntry> = emptyList(),
val removed: List<PluginCatalogRemovedEntry> = emptyList(),
@SerialName("generated_at") val generatedAt: String? = null,
)
@Serializable
@@ -80,6 +80,7 @@ import com.m57.hermescontrol.data.model.OAuthSubmitResponse
import com.m57.hermescontrol.data.model.PairingApproveRequest
import com.m57.hermescontrol.data.model.PairingResponse
import com.m57.hermescontrol.data.model.PairingRevokeRequest
import com.m57.hermescontrol.data.model.PluginCatalogResponse
import com.m57.hermescontrol.data.model.PluginProvidersPutRequest
import com.m57.hermescontrol.data.model.PluginsHubResponse
import com.m57.hermescontrol.data.model.PortalResponse
@@ -625,6 +626,9 @@ interface HermesApiService {
@GET("api/dashboard/plugins/hub")
suspend fun getPlugins(): Response<PluginsHubResponse>
@GET("api/dashboard/plugins/catalog")
suspend fun getPluginCatalog(): Response<PluginCatalogResponse>
@POST("api/dashboard/agent-plugins/install")
suspend fun installPlugin(
@Body body: AgentPluginInstallBody,