Validated on the combined batch board over tip 92ef3c71: static gates clean (changelog, file-size, complexity 2624<=2774, cognitive 1182<=1223, dead-code 411<=416), typecheck:core clean, focused tests green.
Combo without models is now refused at the schema boundary (API 400), the CLI flags it, and openapi.yaml matches the real contract (phantom props removed). combo-* suites + cli-combo-create-models green on the board. Closes#10954. Thank you @maxmad64bis!
⭐5 — Fix(#10954): `combo create` via CLI sempre criava combos vazios (models: [] hardcoded, sem flag). Adiciona --models/--model com parser próprio (CLI .mjs sem alias @/). TDD RED→GREEN, 22/22 testes verdes. UNSTABLE é o base-red inherited #9985.
Three commands turned a transport failure into something that reads as real
state:
- `keys add` aborted on any 4xx. `/api/v1/providers/keys` is not mounted on
the shipped server, so a 404 stranded the user with "HTTP 404" while the
SQLite fallback directly below it — which works — was unreachable whenever the
server was up. New isRouteUnavailableStatus() (404/405/501) lets the caller
fall through; genuine client errors (400/401/403/409/422/429) stay fatal.
- `providers test-all` reported every OAuth connection as FAILED because
getProviderApiKey() throws for non-apikey connections by design — and
persisted that verdict to provider_connections.test_status, marking healthy
OAuth providers broken. Those connections are now skipped. An "unsupported"
probe result (no recipe in PROVIDER_TEST_CONFIGS) is likewise a CLI gap, not
a provider failure, so it no longer overwrites a good test_status.
- `combo list` printed "No combos configured" when /api/combos returned
non-2xx, which is indistinguishable from genuine emptiness. It now reports the
status and exits non-zero.
Refs #10081
Extrai 7 grupos de comandos do monolito bin/cli-commands.mjs (2853 linhas)
para módulos individuais em bin/cli/commands/, registrados via Commander.
- stop.mjs: SIGTERM/SIGKILL via process.kill(); fallback por porta usa execFile
com array de args (evita injeção de shell / Semgrep CWE-78)
- restart.mjs: delega para runStopCommand + runServe
- dashboard.mjs: alias "open", fallback nativo por plataforma via execFile
- keys.mjs: server-first (POST /api/v1/providers/keys) → DB fallback; valida
provider via loadAvailableProviders(); suporte a --stdin
- models.mjs: GET /api/models → fallback /api/v1/models; filtro por provider e --search
- combo.mjs: list/switch/create/delete; switch server-first → DB fallback via key_value;
TODO(1.5) marcados para substituir SQL cru por src/lib/db/combos.ts
- serve.mjs: escreve PID via writePidFile() no spawn; limpa no shutdown; suporte daemon
utils/pid.mjs e i18n keys (en.json + pt-BR.json) já criados em iteração anterior.
Testes: cli-keys-command.test.ts atualizado para novos runners;
cli-serve-stop-command.test.ts, cli-combo-command.test.ts,
cli-models-command.test.ts adicionados (23 testes, 0 falhas).