use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct SyncAssetsResult {
id: String,
agents_deployed: Vec<String>,
agents_skipped: Vec<String>,
skills_deployed: Vec<String>,
skills_skipped: Vec<String>,
output_style_synced: bool,
}
#[derive(Debug, Deserialize)]
struct SyncAllAssetsResult {
synced: Vec<SyncAssetsResult>,
skipped: Vec<String>,
errors: Vec<String>,
}
fn render_result(r: &SyncAssetsResult) {
if r.agents_deployed.is_empty() && r.skills_deployed.is_empty() {
println!("{}: already up to date", r.id);
return;
}
print!(
"{}: {} agent(s), {} skill(s) refreshed",
r.id,
r.agents_deployed.len(),
r.skills_deployed.len()
);
if !r.agents_skipped.is_empty() || !r.skills_skipped.is_empty() {
print!(
" ({} agent(s), {} skill(s) skipped — user-modified)",
r.agents_skipped.len(),
r.skills_skipped.len()
);
}
if r.output_style_synced {
print!(", output style synced");
}
println!();
}
pub(crate) async fn session_sync_assets(
client: &reqwest::Client,
url: &str,
id_or_name: String,
) -> anyhow::Result<()> {
let Some(id) = super::managed_route::resolve_managed_match(client, url, &id_or_name).await
else {
anyhow::bail!("managed session '{id_or_name}' not found");
};
let resp = client
.post(format!("{url}/api/v1/sessions/managed/{id}/sync-assets"))
.send()
.await?;
if resp.status() == reqwest::StatusCode::NOT_FOUND {
anyhow::bail!("managed session '{id}' not found");
}
let result: SyncAssetsResult = resp.error_for_status()?.json().await?;
render_result(&result);
Ok(())
}
pub(crate) async fn session_sync_assets_all(
client: &reqwest::Client,
url: &str,
) -> anyhow::Result<()> {
let resp = client
.post(format!("{url}/api/v1/sessions/managed/sync-assets"))
.send()
.await?;
let result: SyncAllAssetsResult = resp.error_for_status()?.json().await?;
if result.synced.is_empty() {
println!("no syncable sessions found");
}
for r in &result.synced {
render_result(r);
}
if !result.skipped.is_empty() {
println!(
"{} session(s) skipped (provisioning/decommissioned)",
result.skipped.len()
);
}
for err in &result.errors {
eprintln!("error: {err}");
}
if !result.errors.is_empty() {
anyhow::bail!(
"{} of {} session(s) failed to sync",
result.errors.len(),
result.synced.len() + result.errors.len()
);
}
Ok(())
}
#[cfg(test)]
#[path = "sync_assets_tests.rs"]
mod tests;