magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
use std::{
    thread::JoinHandle,
    time::{Duration, Instant},
};

use super::MissionControlApp;
use crate::{
    providers::claude_usage::{ClaudeUsage, connected_account, load_claude_usage, quota_label},
    tui::{TuiEvent, state::MissionControlState},
};

const REFRESH_INTERVAL: Duration = Duration::from_secs(300);
const CONNECTION_CHECK_INTERVAL: Duration = Duration::from_secs(5);

#[derive(Default)]
pub(super) struct ClaudeQuotaRefresh {
    worker: Option<JoinHandle<QuotaCheck>>,
    account: Option<[u8; 32]>,
    last_attempt: Option<Instant>,
    usage: Option<ClaudeUsage>,
    last_label_update: Option<Instant>,
    last_check: Option<Instant>,
    manual_refresh: bool,
}

struct QuotaCheck {
    account: Option<[u8; 32]>,
    usage: Option<ClaudeUsage>,
    attempted_at: Option<Instant>,
}

impl ClaudeQuotaRefresh {
    pub(super) fn request_refresh(&mut self) {
        self.manual_refresh = true;
    }

    pub(super) fn cleanup(&mut self) -> Option<String> {
        let worker = self.worker.take()?;
        match crate::thread_join::join_with_timeout(worker, Duration::from_millis(50)) {
            Ok(Err(_)) => Some("Claude usage worker panicked".into()),
            _ => None,
        }
    }
}

impl MissionControlApp {
    pub(super) fn poll_claude_quota(
        &mut self,
        state: &mut MissionControlState,
        now: Instant,
    ) -> bool {
        let quota = &mut self.claude_quota;
        let mut changed = false;
        if quota
            .worker
            .as_ref()
            .is_some_and(|worker| worker.is_finished())
        {
            let check = quota
                .worker
                .take()
                .expect("finished worker")
                .join()
                .unwrap_or(QuotaCheck {
                    account: None,
                    usage: None,
                    attempted_at: Some(now),
                });
            let old_label = state.claude_quota_label.clone();
            if quota.account != check.account {
                quota.account = check.account;
                quota.last_attempt = None;
                quota.usage = None;
                state.claude_quota_label = None;
            }
            if let Some(attempted_at) = check.attempted_at {
                quota.last_attempt = Some(attempted_at);
                quota.usage = check.usage;
            }
            state.claude_quota_label = quota
                .usage
                .as_ref()
                .and_then(|usage| quota_label(usage, chrono::Utc::now()));
            quota.last_label_update = Some(now);
            changed = old_label != state.claude_quota_label;
        }
        if let Some(usage) = quota.usage.as_ref()
            && quota
                .last_label_update
                .is_none_or(|last| now.duration_since(last) >= Duration::from_secs(1))
        {
            quota.last_label_update = Some(now);
            let label = quota_label(usage, chrono::Utc::now());
            if label != state.claude_quota_label {
                state.claude_quota_label = label;
                changed = true;
            }
        }
        let refresh_due = quota.account.is_some()
            && quota
                .last_attempt
                .is_none_or(|last| now.duration_since(last) >= REFRESH_INTERVAL)
            && state
                .last_agent_activity
                .is_some_and(|last| now.duration_since(last) < REFRESH_INTERVAL);
        let check_due = quota
            .last_check
            .is_none_or(|last| now.duration_since(last) >= CONNECTION_CHECK_INTERVAL);
        if quota.worker.is_none() && (check_due || refresh_due || quota.manual_refresh) {
            let previous = quota.account;
            let force = quota.manual_refresh || refresh_due;
            quota.manual_refresh = false;
            quota.last_check = Some(now);
            let sender = self.events.clone();
            quota.worker = Some(std::thread::spawn(move || {
                let account = connected_account();
                let mut check = QuotaCheck {
                    account,
                    usage: None,
                    attempted_at: None,
                };
                if account.is_some() && (force || previous.is_none()) {
                    check.attempted_at = Some(Instant::now());
                    let usage = load_claude_usage();
                    check.account = connected_account();
                    if check.account == account {
                        check.usage = usage.ok();
                    }
                }
                let _ = sender.try_send(TuiEvent::WorkerOutcomeReady);
                check
            }));
        }
        changed
    }
}