use log::debug;
pub use crate::cm_approval::{
ApprovalRequestSpec, InteractiveGateOutcome, SensitiveCapability, SharedAllowlistHandles,
ToolApprovalWebError, WebApprovalChannelMode, WebApprovalSink, persist_allowlist_key,
run_web_tool_approval,
};
use crate::cm_types::CommandApprovalDecision;
pub fn web_tool_runtime_approval_sink(
rt: &crate::cm_tools::tool_runtime::WebToolRuntime,
) -> WebApprovalSink<'_> {
WebApprovalSink {
out_tx: &rt.out_tx,
approval_rx_shared: &rt.approval_rx_shared,
approval_request_guard: &rt.approval_request_guard,
}
}
pub async fn interactive_gate_after_whitelist_miss(
web: Option<WebApprovalSink<'_>>,
spec: &ApprovalRequestSpec,
sse_log_label: &'static str,
allowlist: &SharedAllowlistHandles<'_>,
) -> Result<InteractiveGateOutcome, ToolApprovalWebError> {
let decision = request_tool_interactive_approval(web, spec, sse_log_label).await?;
match decision {
CommandApprovalDecision::Deny => Ok(InteractiveGateOutcome::Denied(format!(
"用户拒绝 {}:{}",
spec.sse_command,
spec.sse_args.trim()
))),
CommandApprovalDecision::AllowOnce => Ok(InteractiveGateOutcome::Allowed),
CommandApprovalDecision::AllowAlways => {
if let Some(k) = spec.allowlist_key.as_deref() {
persist_allowlist_key(allowlist, k).await;
}
Ok(InteractiveGateOutcome::Allowed)
}
}
}
pub async fn request_tool_interactive_approval(
web: Option<WebApprovalSink<'_>>,
spec: &ApprovalRequestSpec,
sse_log_label: &'static str,
) -> Result<CommandApprovalDecision, ToolApprovalWebError> {
if let Some(sink) = web {
debug!(
target: "crabmate",
"tool_approval web capability={:?} title={}",
spec.capability,
spec.cli_title
);
return run_web_tool_approval(sink, spec, sse_log_label, WebApprovalChannelMode::Strict)
.await;
}
Err(ToolApprovalWebError::ChannelUnavailable)
}