feat: richer Webhooks tab — create, toggle, delete subscriptions (#454)

* feat: richer Webhooks tab — create, toggle, delete subscriptions

Adds full webhook subscription management matching the dashboard UI:
- Create subscription dialog (name, description, events, deliver, secret)
- Per-subscription enable/disable toggle via PUT endpoint
- Delete with confirmation dialog
- Enhanced subscription cards: description, event chips, delivery badge,
  secret indicator, URL display
- Search filtering across name, description, and events
- Updated Webhook.kt model with full API shape
- New HermesApiService endpoints for CRUD operations

Closes #447

* fix: remove double top padding on webhooks screen LazyColumn

* fix: use string resources in webhooks screen (sweep hardcoded strings)

* fix: resolve unresolved merge conflict in AchievementsScreen

Leftover <<<<<<< markers from a main merge broke compilation on the
webhooks-full-parity branch, cascading through ktlint/lint/tests.

---------

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-07-03 00:52:49 +03:00
1 parent f887146056
commit d875c8a040
6 files changed
+660 -161

No files matched your search

@@ -8,10 +8,38 @@ data class WebhooksResponse(
data class WebhookSubscription(
val name: String,
val url: String,
val description: String?,
val events: List<String>?,
val deliver: String?,
val deliver_only: Boolean?,
val prompt: String?,
val skills: List<String>?,
val created_at: String?,
val url: String,
val secret_set: Boolean?,
val enabled: Boolean?,
)
data class CreateWebhookRequest(
val name: String,
val description: String? = null,
val events: List<String>? = null,
val prompt: String? = null,
val skills: List<String>? = null,
val deliver: String? = null,
val deliver_only: Boolean? = null,
val deliver_chat_id: String? = null,
val secret: String? = null,
)
data class WebhooksToggleRequest(
val enabled: Boolean,
)
data class WebhookToggleSubscriptionRequest(
val enabled: Boolean,
)
data class DeleteWebhookResponse(
val ok: Boolean,
)
@@ -5,7 +5,9 @@ import com.m57.hermescontrol.data.model.ActiveProfileResponse
import com.m57.hermescontrol.data.model.AgentPluginInstallBody
import com.m57.hermescontrol.data.model.CreateCronJobRequest
import com.m57.hermescontrol.data.model.CreateTaskBody
import com.m57.hermescontrol.data.model.CreateWebhookRequest
import com.m57.hermescontrol.data.model.CronJob
import com.m57.hermescontrol.data.model.DeleteWebhookResponse
import com.m57.hermescontrol.data.model.DoctorResponse
import com.m57.hermescontrol.data.model.EnvVarConfig
import com.m57.hermescontrol.data.model.EnvVarRevealRequest
@@ -45,6 +47,8 @@ import com.m57.hermescontrol.data.model.UpdateCronJobRequest
import com.m57.hermescontrol.data.model.UpdateProfileModelRequest
import com.m57.hermescontrol.data.model.UpdateProfileSoulRequest
import com.m57.hermescontrol.data.model.UpdateRawConfigRequest
import com.m57.hermescontrol.data.model.WebhookSubscription
import com.m57.hermescontrol.data.model.WebhookToggleSubscriptionRequest
import com.m57.hermescontrol.data.model.WebhooksResponse
import com.m57.hermescontrol.data.model.WebhooksToggleRequest
import retrofit2.Response
@@ -245,6 +249,22 @@ interface HermesApiService {
@Body body: WebhooksToggleRequest,
): Response<Unit>
@POST("api/webhooks")
suspend fun createWebhook(
@Body body: CreateWebhookRequest,
): Response<WebhookSubscription>
@DELETE("api/webhooks/{name}")
suspend fun deleteWebhook(
@Path("name") name: String,
): Response<DeleteWebhookResponse>
@PUT("api/webhooks/{name}/enabled")
suspend fun setWebhookEnabled(
@Path("name") name: String,
@Body body: WebhookToggleSubscriptionRequest,
): Response<Unit>
@GET("api/model/options")
suspend fun getModelOptions(): Response<ModelOptionsResponse>