feat(#441): add profile cloning and description editing (#463)

* feat(#441): add profile cloning and description editing

* feat(#441): add profile builder wizard and API enhancements

Introduce 5-step Profile Builder wizard into ProfilesScreen and add support for create/clone endpoints.

* fix(#441): restore imports and add ProfileInfo constructor defaults

* style: run ktlint format on codebase to fix styling errors

* fix(#441): address PR review comments including provider slug bug, validation, and strings

* fix(#441): remove unused McpServerConfigInput import

---------

Co-authored-by: Hermes Agent <hermes@agent.local>
This commit is contained in:
M57andHermes Agent authored and GitHub committed 2026-07-03 22:24:42 +03:00
1 parent 846a9380e2
commit d181851670
37 files changed
+2299 -448

No files matched your search

@@ -6,14 +6,15 @@ data class ProfilesResponse(
data class ProfileInfo(
val name: String,
val path: String?,
val is_default: Boolean?,
val model: String?,
val provider: String?,
val has_env: Boolean?,
val skill_count: Int?,
val gateway_running: Boolean?,
val description: String?,
val path: String? = null,
val is_default: Boolean? = null,
val model: String? = null,
val provider: String? = null,
val has_env: Boolean? = null,
val skill_count: Int? = null,
val gateway_running: Boolean? = null,
val description: String? = null,
val description_auto: Boolean? = null,
)
data class ActiveProfileResponse(
@@ -37,3 +38,29 @@ data class UpdateProfileModelRequest(
val provider: String,
val model: String,
)
data class CloneProfileRequest(
val name: String,
)
data class UpdateProfileDescriptionRequest(
val description: String,
)
data class CreateProfileRequest(
val name: String,
val description: String?,
val provider: String?,
val model: String?,
val mcp_servers: List<McpServerConfigInput>?,
val keep_skills: Boolean?,
val hub_skills: List<String>?,
)
data class McpServerConfigInput(
val name: String,
val transport: String,
val url: String? = null,
val command: String? = null,
val args: List<String>? = null,
)
@@ -7,3 +7,9 @@ data class Skill(
val enabled: Boolean,
val content: String? = null,
)
data class HubSkill(
val name: String,
val description: String?,
val source: String?,
)
@@ -9,9 +9,11 @@ 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.CloneProfileRequest
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.CreateProfileRequest
import com.m57.hermescontrol.data.model.CreateTaskBody
import com.m57.hermescontrol.data.model.CreateWebhookRequest
import com.m57.hermescontrol.data.model.CredentialPoolResponse
@@ -25,6 +27,7 @@ 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.HubSkill
import com.m57.hermescontrol.data.model.KanbanBoardResponse
import com.m57.hermescontrol.data.model.KanbanBoardsResponse
import com.m57.hermescontrol.data.model.KanbanTask
@@ -67,6 +70,7 @@ 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.UpdateProfileDescriptionRequest
import com.m57.hermescontrol.data.model.UpdateProfileModelRequest
import com.m57.hermescontrol.data.model.UpdateProfileSoulRequest
import com.m57.hermescontrol.data.model.UpdateRawConfigRequest
@@ -146,6 +150,11 @@ interface HermesApiService {
@GET("api/skills")
suspend fun getSkills(): Response<List<Skill>>
@POST("api/skills/hub/search")
suspend fun searchSkillsHub(
@Query("q") q: String,
): Response<List<HubSkill>>
@PUT("api/skills/toggle")
suspend fun toggleSkill(
@Body body: ToggleSkillRequest,
@@ -202,6 +211,11 @@ interface HermesApiService {
@GET("api/profiles")
suspend fun getProfiles(): Response<ProfilesResponse>
@POST("api/profiles")
suspend fun createProfile(
@Body body: CreateProfileRequest,
): Response<Unit>
@GET("api/profiles/active")
suspend fun getActiveProfile(): Response<ActiveProfileResponse>
@@ -227,6 +241,18 @@ interface HermesApiService {
@Body body: UpdateProfileModelRequest,
): Response<Unit>
@POST("api/profiles/{name}/clone")
suspend fun cloneProfile(
@Path("name") name: String,
@Body body: CloneProfileRequest,
): Response<Unit>
@PUT("api/profiles/{name}/description")
suspend fun updateProfileDescription(
@Path("name") name: String,
@Body body: UpdateProfileDescriptionRequest,
): Response<Unit>
@GET("api/tools/toolsets")
suspend fun getToolsets(): Response<List<Toolset>>
@@ -49,7 +49,10 @@ fun mapHttpError(
val detail =
if (errorBody != null) {
try {
val parsed = com.google.gson.JsonParser.parseString(errorBody).asJsonObject
val parsed =
com.google.gson.JsonParser
.parseString(errorBody)
.asJsonObject
parsed.get("detail")?.asString
} catch (_: Exception) {
null