feat: implémentation et QA ticket99 - agent model configuration v2

- backend A-C: VO Codex/Claude, renderers/projecteurs modèle, SecretRef/env, catalogues/use cases/Tauri commands
- frontend D: profils Codex/Claude provider->model->secret, validations, conservation SecretRef
- fix: app-tauri embedded_server isolant IDEA_WEB_ROOT

QA: backend 57/57 tests, frontend 74/74 tests OK
This commit is contained in:
2026-07-26 11:56:25 +02:00
parent 13fb538880
commit dae07d35bb
27 changed files with 2358 additions and 143 deletions

View File

@ -999,10 +999,13 @@ use application::{
CloneOpenCodeProfileFromSeedInput, CloneOpenCodeProfileFromSeedOutput, ConfigureProfilesInput,
ConfigureProfilesOutput, DeleteProfileInput, DetectProfilesInput, DetectProfilesOutput,
FirstRunStateOutput, ListProfilesOutput, ProfileAvailability, ReferenceProfilesOutput,
SaveOpenCodeProviderProfileInput, SaveOpenCodeProviderProfileOutput, SaveProfileInput,
SaveProfileOutput,
SaveClaudeProviderProfileInput, SaveClaudeProviderProfileOutput, SaveCodexProviderProfileInput,
SaveCodexProviderProfileOutput, SaveOpenCodeProviderProfileInput,
SaveOpenCodeProviderProfileOutput, SaveProfileInput, SaveProfileOutput,
};
use domain::profile::{
AgentProfile, CodexCustomProviderConfig, CustomProviderConfig, OpenCodeConfig,
};
use domain::profile::{AgentProfile, CustomProviderConfig, OpenCodeConfig};
use domain::ProfileId;
/// A profile crossing the wire. [`AgentProfile`] already serialises camelCase
@ -1069,6 +1072,68 @@ impl From<application::OpenCodeProviderCatalogEntry> for OpenCodeProviderDto {
}
}
/// One entry of the static Codex provider catalogue (ticket #99).
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodexProviderDto {
pub provider_id: String,
pub display_name: String,
pub models: Vec<String>,
pub custom_supported: bool,
}
impl From<application::CodexProviderCatalogEntry> for CodexProviderDto {
fn from(entry: application::CodexProviderCatalogEntry) -> Self {
Self {
provider_id: entry.provider_id,
display_name: entry.display_name,
models: entry.models,
custom_supported: entry.custom_supported,
}
}
}
/// A list of Codex provider catalogue entries.
#[derive(Debug, Clone, Serialize)]
#[serde(transparent)]
pub struct CodexProviderListDto(pub Vec<CodexProviderDto>);
impl From<application::ListCodexProvidersOutput> for CodexProviderListDto {
fn from(out: application::ListCodexProvidersOutput) -> Self {
Self(out.providers.into_iter().map(Into::into).collect())
}
}
/// One entry of the static Claude provider catalogue (ticket #99).
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClaudeProviderDto {
pub provider_id: String,
pub display_name: String,
pub models: Vec<String>,
}
impl From<application::ClaudeProviderCatalogEntry> for ClaudeProviderDto {
fn from(entry: application::ClaudeProviderCatalogEntry) -> Self {
Self {
provider_id: entry.provider_id,
display_name: entry.display_name,
models: entry.models,
}
}
}
/// A list of Claude provider catalogue entries.
#[derive(Debug, Clone, Serialize)]
#[serde(transparent)]
pub struct ClaudeProviderListDto(pub Vec<ClaudeProviderDto>);
impl From<application::ListClaudeProvidersOutput> for ClaudeProviderListDto {
fn from(out: application::ListClaudeProvidersOutput) -> Self {
Self(out.providers.into_iter().map(Into::into).collect())
}
}
/// A list of OpenCode cloud-provider catalogue entries (camelCase array on the
/// wire).
#[derive(Debug, Clone, Serialize)]
@ -1181,6 +1246,63 @@ impl From<SaveOpenCodeProviderProfileOutput> for ProfileDto {
}
}
/// Request DTO for `save_codex_provider_profile` (ticket #99).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SaveCodexProviderProfileRequestDto {
pub profile: AgentProfile,
pub provider_id: String,
pub model: String,
pub api_key: String,
#[serde(default)]
pub custom: Option<CodexCustomProviderConfig>,
}
impl From<SaveCodexProviderProfileRequestDto> for SaveCodexProviderProfileInput {
fn from(dto: SaveCodexProviderProfileRequestDto) -> Self {
Self {
profile: dto.profile,
provider_id: dto.provider_id,
model: dto.model,
api_key: dto.api_key,
custom: dto.custom,
}
}
}
impl From<SaveCodexProviderProfileOutput> for ProfileDto {
fn from(out: SaveCodexProviderProfileOutput) -> Self {
Self(out.profile)
}
}
/// Request DTO for `save_claude_provider_profile` (ticket #99).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SaveClaudeProviderProfileRequestDto {
pub profile: AgentProfile,
pub provider_id: String,
pub model: String,
pub api_key: String,
}
impl From<SaveClaudeProviderProfileRequestDto> for SaveClaudeProviderProfileInput {
fn from(dto: SaveClaudeProviderProfileRequestDto) -> Self {
Self {
profile: dto.profile,
provider_id: dto.provider_id,
model: dto.model,
api_key: dto.api_key,
}
}
}
impl From<SaveClaudeProviderProfileOutput> for ProfileDto {
fn from(out: SaveClaudeProviderProfileOutput) -> Self {
Self(out.profile)
}
}
/// Request DTO for `clone_opencode_profile_from_seed`.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]