feat: add config profiles

- Add ConfigProfile and ConfigProfileInclude data models with migrations
- Implement profile resolver service with ordered includes and merge rules
- Add profile CRUD API with validation, compatibility, and cycle detection
- Add instance API plumbing for profile selection on create/start/restart
- Add resolved profile preview and default resolution APIs
- Add frontend config profile API client and management UI
- Add launch/restart profile selection UI
- Add backend integration and unit tests (31 passing)

OpenSpec: add-config-profiles
Quality gates: ruff, TypeScript compile, 31 tests passing
This commit is contained in:
2026-05-24 17:58:39 +00:00
parent 4de312c170
commit 9ad11a021c
23 changed files with 3136 additions and 125 deletions
+12 -5
View File
@@ -10,6 +10,7 @@ export interface ToolInstance {
status: string;
url: string | null;
port: number | null;
selected_config_profile_id: string | null;
created_at: string;
}
@@ -49,7 +50,8 @@ export async function createInstance(
displayName?: string,
cloneMode?: string,
branch?: string,
newBranch?: string
newBranch?: string,
configProfileId?: string
): Promise<ToolInstance> {
const response = await apiClient.post(
`/projects/${projectId}/repositories/${repoId}/instances`,
@@ -59,6 +61,7 @@ export async function createInstance(
clone_mode: cloneMode || "mount",
branch: branch || undefined,
new_branch: newBranch || undefined,
config_profile_id: configProfileId,
}
);
return response.data;
@@ -67,10 +70,12 @@ export async function createInstance(
export async function startInstance(
projectId: string,
repoId: string,
instanceId: string
instanceId: string,
configProfileId?: string
): Promise<{ status: string; url?: string }> {
const response = await apiClient.post(
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/start`
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/start`,
{ config_profile_id: configProfileId }
);
return response.data;
}
@@ -89,10 +94,12 @@ export async function stopInstance(
export async function restartInstance(
projectId: string,
repoId: string,
instanceId: string
instanceId: string,
configProfileId?: string
): Promise<{ status: string; url?: string }> {
const response = await apiClient.post(
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/restart`
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/restart`,
{ config_profile_id: configProfileId }
);
return response.data;
}