feat(dashboard): batch POST /api/permissions for save-all perms (#1719)

This commit is contained in:
damocles 2026-06-17 18:08:19 +02:00
commit 8e24814efe
5 changed files with 163 additions and 0 deletions

View file

@ -69,6 +69,16 @@ pub enum PermPayload {
ToolGroups { groups: Vec<String> },
/// Set the capabilities for one agent (`capabilities.json`).
Capabilities { caps: Vec<String> },
/// Set both perm-types for one agent in a single entry — the batch
/// `POST /api/permissions` path. Either field `None` leaves that
/// file untouched (no write, no commit); the worker commits whichever
/// are present in one git commit, then rebuilds once. Collapses the
/// dedup key to `(kind, agent)` so caps + groups for one agent
/// produce a single rebuild rather than two.
Combined {
groups: Option<Vec<String>>,
caps: Option<Vec<String>>,
},
}
/// Where the enqueue request originated. Drives the "why" chip on the
@ -411,6 +421,9 @@ impl RebuildQueue {
) | (
Some(PermPayload::Capabilities { .. }),
Some(PermPayload::Capabilities { .. })
) | (
Some(PermPayload::Combined { .. }),
Some(PermPayload::Combined { .. })
) | (None, None)
);
if entry.state == QueueState::Queued
@ -791,6 +804,21 @@ async fn dispatch(
.with_context(|| format!("commit capabilities for {name}"))?;
coord.emit_capabilities_snapshot();
}
Some(PermPayload::Combined { groups, caps }) => {
// Batch perm change: commit whichever file(s) are
// present in a single git commit, then the rebuild
// below runs once — no double-rebuild for an agent
// whose caps AND groups both changed.
crate::meta::commit_perms(name, groups.as_deref(), caps.as_deref())
.await
.with_context(|| format!("commit perms for {name}"))?;
if groups.is_some() {
coord.emit_tool_groups_snapshot();
}
if caps.is_some() {
coord.emit_capabilities_snapshot();
}
}
None => {
anyhow::bail!(
"PermChange entry id={} agent={} is missing perm_payload",