feat(login): custom provider logins fetch and select models

After the base URL, name, and key steps, the login now opens a model
selection screen instead of blindly saving. The listing fetch runs
concurrently with a manual-id input — a WAF challenge page or a 404 no
longer passes validation silently: a base without a /vN tail is probed
once as {base}/v1 and the working base is adopted, an anthropic-wire
listing that 401s on x-api-key retries once with Bearer (relays gate
GET /models on Bearer while /messages takes the key header), and a
failed fetch shows its reason while manual add stays usable. The chosen
ids persist as [platforms.<name>] models = [...]; catalog assembly
synthesizes selected ids the listing never served (before [model.*]
overrides so they compose) and filters unselected provider entries
(after overrides, authoritative in the provider namespace). Provider-
owned entries keep their declared wire against donor backend
inheritance, and listing errors quote line/column only — a reflected
body could carry the key.
This commit is contained in:
雷电芽衣 committed 2026-09-29 22:00:20 -04:00
1 parent 1d631a07fa
commit 9a2a9eac99
20 files changed
+1655 -68

No files matched your search

+22 -3
View File
@@ -1915,13 +1915,15 @@ pub(crate) fn execute(
request_seq,
provider,
key,
models,
} => {
let tx = acp_tx.clone();
let abort_handle = tasks.spawn(async move {
// Persist first so authenticate finds the definition and key.
if let Err(e) =
kigi_shell::agent::custom_providers::save_custom_provider(&provider, &key)
.await
if let Err(e) = kigi_shell::agent::custom_providers::save_custom_provider(
&provider, &key, &models,
)
.await
{
let error = format!("Couldn't save the provider: {e:#}");
ulog::error(
@@ -1940,6 +1942,23 @@ pub(crate) fn execute(
});
meta.auth_abort_handle = Some((request_seq, abort_handle));
}
Effect::FetchCustomProviderModels {
request_seq,
provider,
key,
} => {
tasks.spawn(async move {
let result = tokio::task::spawn_blocking(move || {
kigi_shell::agent::custom_providers::fetch_listing_for_login(&provider, &key)
})
.await
.unwrap_or_else(|e| Err(format!("listing task failed: {e}")));
TaskResult::CustomProviderModelsListed {
request_seq,
result,
}
});
}
Effect::SubmitAuthCode { request_seq, code } => {
let tx = acp_tx.clone();
tasks