Ensure all required providers exist. `explicit_names` are provider **names** supplied via `--provider`. They are passed through directly; the server validates they exist at sandbox creation. `inferred_types` are provider **types** inferred from the trailing command (e.g. `claude` -> type `"claude-code"`). These are resolved to provider names via a type→name lookup, and missing types may be auto-
(
client: &mut crate::tls::GrpcClient,
explicit_names: &[String],
inferred_types: &[String],
auto_providers_override: Option<bool>,
)
| 3701 | /// Returns a deduplicated list of provider **names** suitable for |
| 3702 | /// `SandboxSpec.providers`. |
| 3703 | pub async fn ensure_required_providers( |
| 3704 | client: &mut crate::tls::GrpcClient, |
| 3705 | explicit_names: &[String], |
| 3706 | inferred_types: &[String], |
| 3707 | auto_providers_override: Option<bool>, |
| 3708 | ) -> Result<Vec<String>> { |
| 3709 | if explicit_names.is_empty() && inferred_types.is_empty() { |
| 3710 | return Ok(Vec::new()); |
| 3711 | } |
| 3712 | |
| 3713 | let mut configured_names: Vec<String> = Vec::new(); |
| 3714 | let mut seen_names: HashSet<String> = HashSet::new(); |
| 3715 | |
| 3716 | // ── Fetch all existing providers ───────────────────────────────────── |
| 3717 | // Build both a name set (for explicit --provider lookups) and a |
| 3718 | // type-to-name map (for inferred provider resolution). |
| 3719 | let mut known_names: HashSet<String> = HashSet::new(); |
| 3720 | let mut type_to_name: HashMap<String, String> = HashMap::new(); |
| 3721 | { |
| 3722 | let mut offset = 0_u32; |
| 3723 | let limit = 100_u32; |
| 3724 | loop { |
| 3725 | let response = client |
| 3726 | .list_providers(ListProvidersRequest { limit, offset }) |
| 3727 | .await |
| 3728 | .into_diagnostic()?; |
| 3729 | let providers = response.into_inner().providers; |
| 3730 | for provider in &providers { |
| 3731 | known_names.insert(provider.object_name().to_string()); |
| 3732 | if !provider.r#type.is_empty() { |
| 3733 | let type_lower = provider.r#type.to_ascii_lowercase(); |
| 3734 | type_to_name |
| 3735 | .entry(type_lower) |
| 3736 | .or_insert_with(|| provider.object_name().to_string()); |
| 3737 | } |
| 3738 | } |
| 3739 | if providers.len() < limit as usize { |
| 3740 | break; |
| 3741 | } |
| 3742 | offset = offset.saturating_add(limit); |
| 3743 | } |
| 3744 | } |
| 3745 | |
| 3746 | // ── Explicit provider names ────────────────────────────────────────── |
| 3747 | // If the name exists on the server, use it directly. Otherwise, if the |
| 3748 | // name matches a known provider type, auto-create a provider of that |
| 3749 | // type with the requested name. |
| 3750 | for name in explicit_names { |
| 3751 | if known_names.contains(name) { |
| 3752 | if seen_names.insert(name.clone()) { |
| 3753 | configured_names.push(name.clone()); |
| 3754 | } |
| 3755 | } else if let Some(provider_type) = normalize_provider_type(name) { |
| 3756 | auto_create_provider( |
| 3757 | client, |
| 3758 | provider_type, |
| 3759 | Some(name), |
| 3760 | auto_providers_override, |