返回 CodeWhale
provider.rs
根目录 / crates / tui / src / tui / setup / provider.rs
1 use codewhale_config::{SetupState, SetupStep, StepEntry, StepStatus};
2
3 use crate::provider_readiness::ResolvedProviderReadiness;
4 use crate::tui::app::App;
5
6 pub(super) fn step_status(readiness: &ResolvedProviderReadiness) -> StepStatus {
7 if matches!(readiness, ResolvedProviderReadiness::Ready) {
8 StepStatus::Verified
9 } else if readiness.can_attempt()
10 && !matches!(
11 readiness,
12 ResolvedProviderReadiness::SavedLastCheckFailed { .. }
13 )
14 {
15 StepStatus::Configured
16 } else {
17 StepStatus::NeedsAction
18 }
19 }
20
21 pub(super) fn step_entry(
22 status: StepStatus,
23 checkpoint_version: &str,
24 result: impl Into<String>,
25 ) -> StepEntry {
26 StepEntry::new(status, true, checkpoint_version).with_result(result.into())
27 }
28
29 /// First launch uses the App's resolved route and credential decision, never a
30 /// sidecar's age or the built-in provider default. This records configuration
31 /// only: no connection/model probe is performed and no prior review is erased.
32 pub(crate) async fn record_configured_route(app: &App) -> anyhow::Result<()> {
33 if !app.startup_route_configured || app.onboarding_needs_api_key || app.model.trim().is_empty()
34 {
35 return Ok(());
36 }
37 let entry = step_entry(
38 StepStatus::Configured,
39 super::CONSTITUTION_CHECKPOINT_VERSION,
40 format!(
41 "provider={}, model={}; configured, not checked",
42 app.provider_identity_for_persistence(),
43 app.model,
44 ),
45 );
46 tokio::task::spawn_blocking(move || {
47 // Existing users keep the inherited setup derivation until they
48 // explicitly review a step; do not create an otherwise empty sidecar.
49 if crate::tui::onboarding::is_onboarded() {
50 return Ok(());
51 }
52 SetupState::update_at(&SetupState::path()?, |state| {
53 if state.status(SetupStep::ProviderModel) == StepStatus::NotStarted {
54 state.set_step(SetupStep::ProviderModel, entry);
55 }
56 })
57 })
58 .await?
59 }
60
61 #[cfg(test)]
62 mod tests {
63 use super::*;
64
65 #[test]
66 fn configured_provider_receipt_only_verifies_observed_success() {
67 for readiness in [
68 ResolvedProviderReadiness::SavedUnchecked,
69 ResolvedProviderReadiness::ImportedTokenUnchecked,
70 ResolvedProviderReadiness::LocalUnchecked,
71 ResolvedProviderReadiness::NoAuthUnchecked,
72 ] {
73 assert_eq!(step_status(&readiness), StepStatus::Configured);
74 }
75 assert_eq!(
76 step_status(&ResolvedProviderReadiness::Ready),
77 StepStatus::Verified
78 );
79 for readiness in [
80 ResolvedProviderReadiness::MissingKey,
81 ResolvedProviderReadiness::InvalidRoute,
82 ResolvedProviderReadiness::SavedLastCheckFailed {
83 category: crate::error_taxonomy::ErrorCategory::Authentication,
84 message: "fixture rejection".to_string(),
85 },
86 ] {
87 assert_eq!(step_status(&readiness), StepStatus::NeedsAction);
88 }
89 }
90 }
91
91 lines RUST