feat(ui): profils locaux/LAN OpenAI-compatible dans le first-run et les terminaux (#14)

Câble la surface frontend des profils IA locaux/LAN OpenAI-compatible,
en parité avec l'adapter backend additif (aab4bca).

- domain: types de profil OpenAI-compatible
- first-run: édition/validation du profil dans le FirstRunWizard
- adapters/mock: mock de profil pour les tests
- terminals: rendu des round-trips et erreurs endpoint (role=alert)

Validé QA (frontend GO): typecheck exit 0, vitest 59 fichiers / 566 tests,
0 echec ; couverture timeouts round-trip + erreur endpoint role=alert
prouvees ; pas de regression sur les tests de bail headless.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 22:34:50 +02:00
parent aab4bcafb6
commit d89380cdf0
9 changed files with 656 additions and 10 deletions

View File

@ -1159,6 +1159,23 @@ export const MOCK_REFERENCE_PROFILES: AgentProfile[] = [
detect: "codex --version",
cwdTemplate: "{projectRoot}",
},
{
id: "mock-ollama",
name: "Ollama / OpenAI-compatible local model",
command: "openai-compatible",
args: [],
contextInjection: { strategy: "conventionFile", target: "AGENTS.md" },
detect: null,
cwdTemplate: "{projectRoot}",
structuredAdapter: "openAiCompatible",
chatHttp: {
endpoint: "http://localhost:11434/v1",
model: "qwen2.5-coder",
requestTimeoutMs: 120_000,
connectTimeoutMs: 5_000,
maxToolIterations: 16,
},
},
];
/**

View File

@ -22,14 +22,16 @@ function customProfile(id: string, command: string): AgentProfile {
describe("MockProfileGateway", () => {
it("firstRunState is first-run with only the selectable reference profiles", async () => {
// §17.3/D7: only structured-drivable profiles (Claude/Codex) are offered;
// Gemini/Aider are filtered out of the selection path server-side.
// §17.3/D7: only structured-drivable profiles are offered (Claude/Codex CLIs
// + the OpenAI-compatible local/LAN adapter, ticket #14); Gemini/Aider are
// filtered out of the selection path server-side.
const gw = new MockProfileGateway();
const state = await gw.firstRunState();
expect(state.isFirstRun).toBe(true);
expect(state.referenceProfiles.map((p) => p.command)).toEqual([
"claude",
"codex",
"openai-compatible",
]);
});
@ -51,6 +53,7 @@ describe("MockProfileGateway", () => {
expect(byCommand).toEqual({
claude: true,
codex: false,
"openai-compatible": false,
});
});