use super::*;
use std::collections::BTreeSet;
pub fn dashboard_worker_targets(controller: &Controller) -> Vec<WorkerPollTarget> {
controller
.state
.sessions
.values()
.filter(|session| session_target_is_pollable(session))
.filter_map(|session| {
worker_poll_target(
controller,
session,
crate::database::load_move_operation(&session.id),
)
})
.collect()
}
pub(crate) fn worker_poll_target(
controller: &Controller,
session: &SessionRecord,
operation: Result<Option<mj_core::state::MoveOperation>>,
) -> Option<WorkerPollTarget> {
let spec = match controller.reconnect_command(&session.id) {
Ok(spec) => spec,
Err(error) => {
tracing::warn!(session_id = %session.id, "could not build worker poll target: {error:#}");
return None;
}
};
Some(WorkerPollTarget {
session_id: session.id.clone(),
spec,
worker_recovery: match operation
.and_then(|operation| controller.worker_recovery_plan(&session.id, operation.as_ref()))
{
Ok(plan) => Some(plan),
Err(error) => {
tracing::debug!(session_id = %session.id, "worker recovery target unavailable: {error:#}");
None
}
},
project_memory: match controller.project_memory_sync_target(&session.id) {
Ok(target) => Some(target),
Err(error) => {
tracing::debug!(session_id = %session.id, "project memory target unavailable: {error:#}");
None
}
},
})
}
pub fn dashboard_worker_targets_excluding(
controller: &Controller,
excluded_sessions: &std::collections::BTreeSet<String>,
) -> Vec<WorkerPollTarget> {
let mut targets = dashboard_worker_targets(controller);
targets.retain(|target| !excluded_sessions.contains(&target.session_id));
targets
}
pub fn credential_sync_targets(controller: &Controller) -> Vec<CredentialSyncTarget> {
let schemes = controller
.config
.profiles
.iter()
.map(|(id, profile)| (id.clone(), profile.auth_scheme().is_api_key()))
.collect();
credential_sync_targets_with_auth(controller, &schemes)
}
pub(crate) fn credential_sync_targets_with_auth(
controller: &Controller,
schemes: &BTreeMap<String, bool>,
) -> Vec<CredentialSyncTarget> {
credential_sync_targets_from_sources(
controller,
schemes,
&staged_credential_sessions(controller),
)
}
pub(crate) fn staged_credential_sessions(controller: &Controller) -> BTreeSet<String> {
controller
.state
.sessions
.values()
.filter(|session| {
matches!(
session.state,
SessionState::Running | SessionState::Checkpointing
) && session.target.is_some()
})
.filter(|session| {
crate::controller::local_profile_homes::session_has_a_staged_home_of_its_own(session)
})
.map(|session| session.id.clone())
.collect()
}
pub(crate) fn credential_sync_targets_from_sources(
controller: &Controller,
schemes: &BTreeMap<String, bool>,
staged: &BTreeSet<String>,
) -> Vec<CredentialSyncTarget> {
controller.state.sessions.values()
.filter(|session| staged.contains(&session.id))
.filter_map(|session| {
let profile = controller.config.profiles.get(&session.last_profile)?;
let spec = match controller.reconnect_command(&session.id) {
Ok(spec) => spec,
Err(error) => {
tracing::warn!(session_id = %session.id, "could not build credential sync target: {error:#}");
return None;
}
};
let sync_github_token = target_syncs_github_token(session.target.as_ref());
Some(CredentialSyncTarget {
session_id: session.id.clone(),
profile_id: session.last_profile.clone(),
harness: profile.kind,
profile_home: profile.home.clone(),
authenticates_with_api_key: *schemes.get(&session.last_profile)?,
sync_github_token,
github_app_configured: controller.config.github.app.is_some(),
skills_scope: session.target.as_ref()?.skills_scope(),
spec,
})
})
.collect()
}
pub(super) fn target_syncs_github_token(target: Option<&mj_core::state::TargetLocator>) -> bool {
target.is_some()
&& !matches!(
target,
Some(mj_core::state::TargetLocator::LocalBare { .. })
)
}
pub fn credential_sync_target_is_current(
mut controller: crate::controller::Controller,
candidate: &CredentialSyncTarget,
) -> bool {
controller
.state
.sessions
.retain(|id, _| id == &candidate.session_id);
credential_sync_targets(&controller).contains(candidate)
}