* feat(#434): add system tab model files * feat(#434): add system API endpoints * feat(#434): rewrite SystemViewModel with full parity * feat(#434): add system screen UI and string resources * feat(#434): update tests for new SystemStatsResponse & ViewModel * fix(#434): fix compile errors — nullable Boolean, SmallFilledTonalButton -> FilledTonalButton * fix(#434): fix old SystemStatsResponse refs in ChatViewModel, wrap sections in item{} blocks * fix(#434): rename sections to lowercase, fix LazyListScope extensions * fix(#434): wrap hook dialog in item{}, pass spacing param to actionLogSection * fix(#434): update HermesApiServiceTest for new typed SystemStatsResponse * fix(#434): fix E2E test coroutine timing, remove outdated Gson edge-case test * fix(#434): fix JUnit 4 assertNotNull argument order * fix(#434): simplify HermesApiServiceTest, restore all mocks in E2e test * fix(#434): remove Gson-edge-case test, fix E2E test dispatcher wiring - Removed testGetSystemStats_nonNumericPercent_returnsNull: tests Gson type-coercion behavior, not app logic — throws JsonSyntaxException - Fixed E2eIntegrationTest dispatcher: mock Dispatchers.IO with answers {} that returns the current Dispatchers.Main dynamically, so runTest's per-test dispatcher override is honored by both viewModelScope.launch and async(Dispatchers.IO) blocks → advanceUntilIdle() advances all work - Removed Dispatchers.Main static mock so runTest can override it * fix(#434): fix E2E test dispatcher for SystemViewModel - Removed Dispatchers.setMain + testDispatcher field from setUp - runTest handles Dispatchers.Main automatically — don't override it - Mock Dispatchers.IO with answers { Dispatchers.Main } so it dynamically resolves to whatever Main is at call time (runTest's TestScope dispatcher) - This ensures viewModelScope.launch and async(Dispatchers.IO) both run on the same TestScope dispatcher, making advanceUntilIdle() effective for all work * fix(#434): use runTest(testDispatcher) for SystemViewModel test The SystemViewModel.loadAll() uses async(Dispatchers.IO) inside coroutineScope { }. With the mocked Dispatchers setup, both Main and IO return testDispatcher, but runTest()'s default TestScope uses a different dispatcher instance whose advanceUntilIdle() can't advance testDispatcher's scheduler. Fix: pass testDispatcher to runTest() so the TestScope's scheduler matches testDispatcher's scheduler, making advanceUntilIdle() effective for all async work. * fix(#434): align test assertion with toast message change The SystemViewModel.triggerBackup() toast message was changed from 'Backup triggered successfully' to 'Backup triggered', but the E2eIntegrationTest assertion wasn't updated, causing a ComparisonFailure on the unit-tests CI job. * fix(#434): address review issues - Fix #1: Change triggerBackup() return type from Unit to ActionResponse so backupArchive is captured, enabling the download button - Fix #2: Replace deprecated LocalClipboardManager with LocalClipboard suspend API (removes CI deprecation warning) - Fix #3: Consolidate redundant 'Hermes version' InfoRow in gateway section (sessions + PID shown in one row) - Remove orphaned system_gateway_state_pid string resource - Fix import ordering for ktlint compliance * fix(#434): revert LocalClipboard change, use @Suppress('DEPRECATION') instead LocalClipboard.setText is not available in the Compose version used by the project. Reverting to LocalClipboardManager with a suppress annotation to keep the deprecation warning clean without breaking compilation. --------- Co-authored-by: M57 <hy4ri@users.noreply.github.com> Co-authored-by: Hermes Agent <hermes@agent.local>
This commit is contained in:
21 files changed
+3083
-230
No files matched your search
@@ -2,28 +2,36 @@ package com.m57.hermescontrol.data.remote
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.m57.hermescontrol.data.model.AchievementsResponse
|
||||
import com.m57.hermescontrol.data.model.ActionResponse
|
||||
import com.m57.hermescontrol.data.model.ActionStatusResponse
|
||||
import com.m57.hermescontrol.data.model.ActiveProfileResponse
|
||||
import com.m57.hermescontrol.data.model.AgentPluginInstallBody
|
||||
import com.m57.hermescontrol.data.model.AuxiliaryModelsResponse
|
||||
import com.m57.hermescontrol.data.model.BulkDeleteRequest
|
||||
import com.m57.hermescontrol.data.model.CheckpointsResponse
|
||||
import com.m57.hermescontrol.data.model.ConfigSchemaResponse
|
||||
import com.m57.hermescontrol.data.model.ConfigUpdateRequest
|
||||
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.CredentialPoolResponse
|
||||
import com.m57.hermescontrol.data.model.CronJob
|
||||
import com.m57.hermescontrol.data.model.CuratorResponse
|
||||
import com.m57.hermescontrol.data.model.DebugShareResponse
|
||||
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
|
||||
import com.m57.hermescontrol.data.model.EnvVarRevealResponse
|
||||
import com.m57.hermescontrol.data.model.EnvVarUpdate
|
||||
import com.m57.hermescontrol.data.model.HookResponse
|
||||
import com.m57.hermescontrol.data.model.KanbanBoardResponse
|
||||
import com.m57.hermescontrol.data.model.KanbanBoardsResponse
|
||||
import com.m57.hermescontrol.data.model.KanbanTask
|
||||
import com.m57.hermescontrol.data.model.LogResponse
|
||||
import com.m57.hermescontrol.data.model.McpServerToggleRequest
|
||||
import com.m57.hermescontrol.data.model.McpServersResponse
|
||||
import com.m57.hermescontrol.data.model.MemoryResponse
|
||||
import com.m57.hermescontrol.data.model.MessagingPlatformResponse
|
||||
import com.m57.hermescontrol.data.model.MessagingPlatformTestResult
|
||||
import com.m57.hermescontrol.data.model.MessagingPlatformUpdate
|
||||
@@ -36,6 +44,7 @@ import com.m57.hermescontrol.data.model.PairingResponse
|
||||
import com.m57.hermescontrol.data.model.PairingRevokeRequest
|
||||
import com.m57.hermescontrol.data.model.PluginProvidersPutRequest
|
||||
import com.m57.hermescontrol.data.model.PluginsHubResponse
|
||||
import com.m57.hermescontrol.data.model.PortalResponse
|
||||
import com.m57.hermescontrol.data.model.ProfileSoulResponse
|
||||
import com.m57.hermescontrol.data.model.ProfilesResponse
|
||||
import com.m57.hermescontrol.data.model.PruneRequest
|
||||
@@ -56,6 +65,7 @@ import com.m57.hermescontrol.data.model.SystemStatsResponse
|
||||
import com.m57.hermescontrol.data.model.ToggleSkillRequest
|
||||
import com.m57.hermescontrol.data.model.Toolset
|
||||
import com.m57.hermescontrol.data.model.ToolsetToggleRequest
|
||||
import com.m57.hermescontrol.data.model.UpdateCheckResponse
|
||||
import com.m57.hermescontrol.data.model.UpdateCronJobRequest
|
||||
import com.m57.hermescontrol.data.model.UpdateProfileModelRequest
|
||||
import com.m57.hermescontrol.data.model.UpdateProfileSoulRequest
|
||||
@@ -64,6 +74,7 @@ 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 okhttp3.ResponseBody
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
@@ -418,7 +429,7 @@ interface HermesApiService {
|
||||
): Response<EnvVarRevealResponse>
|
||||
|
||||
@POST("api/ops/backup")
|
||||
suspend fun triggerBackup(): Response<Unit>
|
||||
suspend fun triggerBackup(): Response<ActionResponse>
|
||||
|
||||
@POST("api/ops/doctor")
|
||||
suspend fun runDoctor(): Response<DoctorResponse>
|
||||
@@ -445,4 +456,117 @@ interface HermesApiService {
|
||||
@Query("board") board: String?,
|
||||
@Body task: CreateTaskBody,
|
||||
): Response<KanbanTask>
|
||||
|
||||
// ── Admin: Hermes update ──────────────────────────────────────────
|
||||
@GET("api/hermes/update/check")
|
||||
suspend fun checkHermesUpdate(
|
||||
@Query("force") force: Boolean = false,
|
||||
): Response<UpdateCheckResponse>
|
||||
|
||||
@POST("api/hermes/update")
|
||||
suspend fun updateHermes(): Response<ActionResponse>
|
||||
|
||||
// ── Admin: Portal ─────────────────────────────────────────────────
|
||||
@GET("api/portal")
|
||||
suspend fun getPortal(): Response<PortalResponse>
|
||||
|
||||
// ── Admin: Curator ────────────────────────────────────────────────
|
||||
@GET("api/curator")
|
||||
suspend fun getCurator(): Response<CuratorResponse>
|
||||
|
||||
@PUT("api/curator/paused")
|
||||
suspend fun setCuratorPaused(
|
||||
@Body body: Map<String, Boolean>,
|
||||
): Response<Map<String, Any>>
|
||||
|
||||
@POST("api/curator/run")
|
||||
suspend fun runCurator(): Response<ActionResponse>
|
||||
|
||||
// ── Admin: Memory ─────────────────────────────────────────────────
|
||||
@GET("api/memory")
|
||||
suspend fun getMemory(): Response<MemoryResponse>
|
||||
|
||||
@POST("api/memory/reset")
|
||||
suspend fun resetMemory(
|
||||
@Body body: Map<String, String>,
|
||||
): Response<Map<String, Any>>
|
||||
|
||||
// ── Admin: Credential pool ────────────────────────────────────────
|
||||
@GET("api/credentials/pool")
|
||||
suspend fun getCredentialPool(): Response<CredentialPoolResponse>
|
||||
|
||||
@POST("api/credentials/pool")
|
||||
suspend fun addCredentialPoolEntry(
|
||||
@Body body: Map<String, String>,
|
||||
): Response<Map<String, Any>>
|
||||
|
||||
@DELETE("api/credentials/pool/{provider}/{index}")
|
||||
suspend fun removeCredentialPoolEntry(
|
||||
@Path("provider") provider: String,
|
||||
@Path("index") index: Int,
|
||||
): Response<Map<String, Any>>
|
||||
|
||||
// ── Admin: Operations ─────────────────────────────────────────────
|
||||
@POST("api/ops/security-audit")
|
||||
suspend fun runSecurityAudit(): Response<ActionResponse>
|
||||
|
||||
@POST("api/ops/prompt-size")
|
||||
suspend fun runPromptSize(): Response<ActionResponse>
|
||||
|
||||
@POST("api/ops/dump")
|
||||
suspend fun runDump(): Response<ActionResponse>
|
||||
|
||||
@POST("api/ops/config-migrate")
|
||||
suspend fun runConfigMigrate(): Response<ActionResponse>
|
||||
|
||||
@POST("api/skills/hub/update")
|
||||
suspend fun updateSkillsFromHub(
|
||||
@Body body: Map<String, String> = emptyMap(),
|
||||
): Response<ActionResponse>
|
||||
|
||||
// ── Admin: Backup download ────────────────────────────────────────
|
||||
@GET("api/ops/backup/download")
|
||||
suspend fun downloadBackup(
|
||||
@Query("archive") archive: String,
|
||||
): Response<ResponseBody>
|
||||
|
||||
// ── Admin: Backup import ──────────────────────────────────────────
|
||||
@POST("api/ops/import")
|
||||
suspend fun runImport(
|
||||
@Body body: Map<String, Any>,
|
||||
): Response<ActionResponse>
|
||||
|
||||
// ── Admin: Debug share ────────────────────────────────────────────
|
||||
@POST("api/ops/debug-share")
|
||||
suspend fun runDebugShare(
|
||||
@Body body: Map<String, Any>,
|
||||
): Response<DebugShareResponse>
|
||||
|
||||
// ── Admin: Checkpoints ────────────────────────────────────────────
|
||||
@GET("api/ops/checkpoints")
|
||||
suspend fun getCheckpoints(): Response<CheckpointsResponse>
|
||||
|
||||
@POST("api/ops/checkpoints/prune")
|
||||
suspend fun pruneCheckpoints(): Response<ActionResponse>
|
||||
|
||||
// ── Admin: Shell hooks ────────────────────────────────────────────
|
||||
@GET("api/ops/hooks")
|
||||
suspend fun getHooks(): Response<HookResponse>
|
||||
|
||||
@POST("api/ops/hooks")
|
||||
suspend fun createHook(
|
||||
@Body body: Map<String, Any>,
|
||||
): Response<Map<String, Any>>
|
||||
|
||||
@DELETE("api/ops/hooks")
|
||||
suspend fun deleteHook(
|
||||
@Body body: Map<String, String>,
|
||||
): Response<Map<String, Any>>
|
||||
|
||||
// ── Admin: Action status (log viewer) ────────────────────────────
|
||||
@GET("api/actions/{name}/status")
|
||||
suspend fun getActionStatus(
|
||||
@Path("name") name: String,
|
||||
@Query("lines") lines: Int = 200,
|
||||
): Response<ActionStatusResponse>
|
||||
}
|
||||
Reference in new issue
Block a user