feat(#442): categorized env keys screen with add/remove, search, and gateway restart (#464)

* feat(#442): categorized env keys screen with add/remove, search, and gateway restart

- Group env vars by category (LLM Providers, Tool API Keys, Messaging
  Platforms, Agent Settings, Other) with collapsible AnimatedVisibility
  sections matching MCP screen pattern
- Add new env var form (PUT /api/env) with key/value inputs
- Delete env var via DELETE /api/env with EnvVarDeleteRequest body
- Reveal/hide/edit inline per-card controls
- Search bar filtering across key names and descriptions
- Gateway restart banner with one-click action when changes are dirty
- Track keysChanged flag showing restart prompt after mutations
- Wire delete endpoint in HermesApiService using @HTTP(hasBody=true)
- Add EnvVarDeleteRequest data model class

* fix(#442): update test to match new categorized KeysViewModel API

* fix(#442): satisfy ktlint multiline-expression-wrapping rule

---------

Co-authored-by: Hermes Agent <hermes@agent.local>
This commit is contained in:
M57andHermes Agent authored and GitHub committed 2026-07-03 23:04:06 +03:00
1 parent 5be4075d95
commit e74fe46a5f
6 files changed
+659 -147

No files matched your search

@@ -958,14 +958,14 @@ class E2eIntegrationTest {
coEvery { mockApiService.updateEnvVar(any()) } returns Response.success(Unit)
val viewModel = KeysViewModel()
viewModel.loadKeys(reveal = false)
viewModel.loadKeys()
advanceUntilIdle()
assertEquals(
"value1",
viewModel.uiState.value.envVars["KEY1"]
?.redactedValue,
)
val allVars =
viewModel.uiState.value.categories
.flatMap { it.vars.entries }
.associate { it.key to it.value }
assertEquals("value1", allVars["KEY1"]?.redactedValue)
viewModel.updateKey("KEY1", "newValue")
advanceUntilIdle()