mirror of
https://github.com/openai/codex.git
synced 2026-02-01 22:47:52 +00:00
Compare commits
3 Commits
1271d450b1
...
persist-ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c37276d45d | ||
|
|
03ce86346b | ||
|
|
74425ba6ad |
@@ -1,7 +1,9 @@
|
||||
use crate::config::CONFIG_TOML_FILE;
|
||||
use crate::config::types::McpServerConfig;
|
||||
use crate::config::types::Notice;
|
||||
use crate::protocol::AskForApproval;
|
||||
use anyhow::Context;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::TrustLevel;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -22,6 +24,10 @@ pub enum ConfigEdit {
|
||||
model: Option<String>,
|
||||
effort: Option<ReasoningEffort>,
|
||||
},
|
||||
/// Update the active (or default) approval policy.
|
||||
SetApprovalPolicy { policy: Option<AskForApproval> },
|
||||
/// Update the active (or default) sandbox mode.
|
||||
SetSandboxMode { mode: Option<SandboxMode> },
|
||||
/// Toggle the acknowledgement flag under `[notice]`.
|
||||
SetNoticeHideFullAccessWarning(bool),
|
||||
/// Toggle the Windows world-writable directories warning acknowledgement flag.
|
||||
@@ -265,6 +271,12 @@ impl ConfigDocument {
|
||||
);
|
||||
mutated
|
||||
}),
|
||||
ConfigEdit::SetApprovalPolicy { policy } => Ok(self.write_profile_value(
|
||||
&["approval_policy"],
|
||||
policy.map(|policy| value(policy.to_string())),
|
||||
)),
|
||||
ConfigEdit::SetSandboxMode { mode } => Ok(self
|
||||
.write_profile_value(&["sandbox_mode"], mode.map(|mode| value(mode.to_string())))),
|
||||
ConfigEdit::SetNoticeHideFullAccessWarning(acknowledged) => Ok(self.write_value(
|
||||
Scope::Global,
|
||||
&[Notice::TABLE_KEY, "hide_full_access_warning"],
|
||||
@@ -596,6 +608,19 @@ impl ConfigEditsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_approval_policy(mut self, policy: AskForApproval) -> Self {
|
||||
self.edits.push(ConfigEdit::SetApprovalPolicy {
|
||||
policy: Some(policy),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_sandbox_mode(mut self, mode: SandboxMode) -> Self {
|
||||
self.edits
|
||||
.push(ConfigEdit::SetSandboxMode { mode: Some(mode) });
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_hide_full_access_warning(mut self, acknowledged: bool) -> Self {
|
||||
self.edits
|
||||
.push(ConfigEdit::SetNoticeHideFullAccessWarning(acknowledged));
|
||||
|
||||
@@ -39,10 +39,12 @@ use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::FinalOutput;
|
||||
use codex_core::protocol::ListSkillsResponseEvent;
|
||||
use codex_core::protocol::Op;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
use codex_core::protocol::SessionSource;
|
||||
use codex_core::protocol::SkillErrorInfo;
|
||||
use codex_core::protocol::TokenUsage;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::openai_models::ModelUpgrade;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
@@ -1069,6 +1071,43 @@ impl App {
|
||||
}
|
||||
}
|
||||
}
|
||||
AppEvent::PersistApprovalSelection { approval, sandbox } => {
|
||||
let profile = self.active_profile.as_deref();
|
||||
let Some(sandbox_mode) = Self::sandbox_mode_for_policy(&sandbox) else {
|
||||
tracing::warn!(
|
||||
"Skipping approval persistence for unsupported sandbox policy: {sandbox:?}"
|
||||
);
|
||||
self.chat_widget.add_error_message(
|
||||
"Failed to save approval preference: unsupported sandbox policy."
|
||||
.to_string(),
|
||||
);
|
||||
return Ok(true);
|
||||
};
|
||||
match ConfigEditsBuilder::new(&self.config.codex_home)
|
||||
.with_profile(profile)
|
||||
.set_approval_policy(approval)
|
||||
.set_sandbox_mode(sandbox_mode)
|
||||
.apply()
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
tracing::error!(
|
||||
error = %err,
|
||||
"failed to persist approval selection"
|
||||
);
|
||||
if let Some(profile) = profile {
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to save approvals for profile `{profile}`: {err}"
|
||||
));
|
||||
} else {
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to save approval preferences: {err}"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AppEvent::UpdateAskForApprovalPolicy(policy) => {
|
||||
self.chat_widget.set_approval_policy(policy);
|
||||
}
|
||||
@@ -1310,6 +1349,15 @@ impl App {
|
||||
(!model.starts_with("codex-auto-")).then(|| Self::reasoning_label(reasoning_effort))
|
||||
}
|
||||
|
||||
fn sandbox_mode_for_policy(policy: &SandboxPolicy) -> Option<SandboxMode> {
|
||||
match policy {
|
||||
SandboxPolicy::ReadOnly => Some(SandboxMode::ReadOnly),
|
||||
SandboxPolicy::WorkspaceWrite { .. } => Some(SandboxMode::WorkspaceWrite),
|
||||
SandboxPolicy::DangerFullAccess => Some(SandboxMode::DangerFullAccess),
|
||||
SandboxPolicy::ExternalSandbox { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn token_usage(&self) -> codex_core::protocol::TokenUsage {
|
||||
self.chat_widget.token_usage()
|
||||
}
|
||||
|
||||
@@ -86,6 +86,12 @@ pub(crate) enum AppEvent {
|
||||
effort: Option<ReasoningEffort>,
|
||||
},
|
||||
|
||||
/// Persist the selected approval policy and sandbox policy to the appropriate config.
|
||||
PersistApprovalSelection {
|
||||
approval: AskForApproval,
|
||||
sandbox: SandboxPolicy,
|
||||
},
|
||||
|
||||
/// Open the reasoning selection popup after picking a model.
|
||||
OpenReasoningPopup {
|
||||
model: ModelPreset,
|
||||
|
||||
@@ -3012,6 +3012,7 @@ impl ChatWidget {
|
||||
.notices
|
||||
.hide_full_access_warning
|
||||
.unwrap_or(false);
|
||||
let persist_preset = self.should_persist_approval_preset(&preset);
|
||||
let actions: Vec<SelectionAction> = if requires_confirmation {
|
||||
let preset_clone = preset.clone();
|
||||
vec![Box::new(move |tx| {
|
||||
@@ -3055,15 +3056,27 @@ impl ChatWidget {
|
||||
});
|
||||
})]
|
||||
} else {
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
persist_preset,
|
||||
)
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
persist_preset,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
persist_preset,
|
||||
)
|
||||
};
|
||||
items.push(SelectionItem {
|
||||
name,
|
||||
@@ -3117,22 +3130,40 @@ impl ChatWidget {
|
||||
fn approval_preset_actions(
|
||||
approval: AskForApproval,
|
||||
sandbox: SandboxPolicy,
|
||||
persist: bool,
|
||||
) -> Vec<SelectionAction> {
|
||||
vec![Box::new(move |tx| {
|
||||
let sandbox_clone = sandbox.clone();
|
||||
let sandbox_for_update = sandbox.clone();
|
||||
let sandbox_for_persist = sandbox.clone();
|
||||
tx.send(AppEvent::CodexOp(Op::OverrideTurnContext {
|
||||
cwd: None,
|
||||
approval_policy: Some(approval),
|
||||
sandbox_policy: Some(sandbox_clone.clone()),
|
||||
sandbox_policy: Some(sandbox_for_update.clone()),
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
}));
|
||||
tx.send(AppEvent::UpdateAskForApprovalPolicy(approval));
|
||||
tx.send(AppEvent::UpdateSandboxPolicy(sandbox_clone));
|
||||
tx.send(AppEvent::UpdateSandboxPolicy(sandbox_for_update));
|
||||
if persist {
|
||||
tx.send(AppEvent::PersistApprovalSelection {
|
||||
approval,
|
||||
sandbox: sandbox_for_persist,
|
||||
});
|
||||
}
|
||||
})]
|
||||
}
|
||||
|
||||
fn should_persist_approval_preset(&self, preset: &ApprovalPreset) -> bool {
|
||||
if preset.id != "full-access" {
|
||||
return true;
|
||||
}
|
||||
self.config
|
||||
.notices
|
||||
.hide_full_access_warning
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn preset_matches_current(
|
||||
current_approval: AskForApproval,
|
||||
current_sandbox: &SandboxPolicy,
|
||||
@@ -3202,12 +3233,10 @@ impl ChatWidget {
|
||||
));
|
||||
let header = ColumnRenderable::with(header_children);
|
||||
|
||||
let mut accept_actions = Self::approval_preset_actions(approval, sandbox.clone());
|
||||
accept_actions.push(Box::new(|tx| {
|
||||
tx.send(AppEvent::UpdateFullAccessWarningAcknowledged(true));
|
||||
}));
|
||||
let accept_actions = Self::approval_preset_actions(approval, sandbox.clone(), false);
|
||||
|
||||
let mut accept_and_remember_actions = Self::approval_preset_actions(approval, sandbox);
|
||||
let mut accept_and_remember_actions =
|
||||
Self::approval_preset_actions(approval, sandbox, true);
|
||||
accept_and_remember_actions.push(Box::new(|tx| {
|
||||
tx.send(AppEvent::UpdateFullAccessWarningAcknowledged(true));
|
||||
tx.send(AppEvent::PersistFullAccessWarningAcknowledged);
|
||||
@@ -3312,8 +3341,15 @@ impl ChatWidget {
|
||||
tx.send(AppEvent::SkipNextWorldWritableScan);
|
||||
}));
|
||||
}
|
||||
let persist_approval = preset
|
||||
.as_ref()
|
||||
.is_some_and(|preset| self.should_persist_approval_preset(preset));
|
||||
if let (Some(approval), Some(sandbox)) = (approval, sandbox.clone()) {
|
||||
accept_actions.extend(Self::approval_preset_actions(approval, sandbox));
|
||||
accept_actions.extend(Self::approval_preset_actions(
|
||||
approval,
|
||||
sandbox,
|
||||
persist_approval,
|
||||
));
|
||||
}
|
||||
|
||||
let mut accept_and_remember_actions: Vec<SelectionAction> = Vec::new();
|
||||
@@ -3322,7 +3358,11 @@ impl ChatWidget {
|
||||
tx.send(AppEvent::PersistWorldWritableWarningAcknowledged);
|
||||
}));
|
||||
if let (Some(approval), Some(sandbox)) = (approval, sandbox) {
|
||||
accept_and_remember_actions.extend(Self::approval_preset_actions(approval, sandbox));
|
||||
accept_and_remember_actions.extend(Self::approval_preset_actions(
|
||||
approval,
|
||||
sandbox,
|
||||
persist_approval,
|
||||
));
|
||||
}
|
||||
|
||||
let items = vec![
|
||||
@@ -3427,7 +3467,11 @@ impl ChatWidget {
|
||||
.iter()
|
||||
.find(|preset| preset.id == "read-only")
|
||||
.map(|preset| {
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
self.should_persist_approval_preset(preset),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
};
|
||||
@@ -3507,7 +3551,11 @@ impl ChatWidget {
|
||||
.iter()
|
||||
.find(|preset| preset.id == "read-only")
|
||||
.map(|preset| {
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
self.should_persist_approval_preset(preset),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
@@ -56,11 +56,13 @@ use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::FinalOutput;
|
||||
use codex_core::protocol::ListSkillsResponseEvent;
|
||||
use codex_core::protocol::Op;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
use codex_core::protocol::SessionSource;
|
||||
use codex_core::protocol::SkillErrorInfo;
|
||||
use codex_core::protocol::TokenUsage;
|
||||
use codex_core::terminal::terminal_info;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::openai_models::ModelUpgrade;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
@@ -1845,6 +1847,43 @@ impl App {
|
||||
}
|
||||
}
|
||||
}
|
||||
AppEvent::PersistApprovalSelection { approval, sandbox } => {
|
||||
let profile = self.active_profile.as_deref();
|
||||
let Some(sandbox_mode) = Self::sandbox_mode_for_policy(&sandbox) else {
|
||||
tracing::warn!(
|
||||
"Skipping approval persistence for unsupported sandbox policy: {sandbox:?}"
|
||||
);
|
||||
self.chat_widget.add_error_message(
|
||||
"Failed to save approval preference: unsupported sandbox policy."
|
||||
.to_string(),
|
||||
);
|
||||
return Ok(true);
|
||||
};
|
||||
match ConfigEditsBuilder::new(&self.config.codex_home)
|
||||
.with_profile(profile)
|
||||
.set_approval_policy(approval)
|
||||
.set_sandbox_mode(sandbox_mode)
|
||||
.apply()
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
tracing::error!(
|
||||
error = %err,
|
||||
"failed to persist approval selection"
|
||||
);
|
||||
if let Some(profile) = profile {
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to save approvals for profile `{profile}`: {err}"
|
||||
));
|
||||
} else {
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to save approval preferences: {err}"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AppEvent::UpdateAskForApprovalPolicy(policy) => {
|
||||
self.chat_widget.set_approval_policy(policy);
|
||||
}
|
||||
@@ -2050,6 +2089,15 @@ impl App {
|
||||
(!model.starts_with("codex-auto-")).then(|| Self::reasoning_label(reasoning_effort))
|
||||
}
|
||||
|
||||
fn sandbox_mode_for_policy(policy: &SandboxPolicy) -> Option<SandboxMode> {
|
||||
match policy {
|
||||
SandboxPolicy::ReadOnly => Some(SandboxMode::ReadOnly),
|
||||
SandboxPolicy::WorkspaceWrite { .. } => Some(SandboxMode::WorkspaceWrite),
|
||||
SandboxPolicy::DangerFullAccess => Some(SandboxMode::DangerFullAccess),
|
||||
SandboxPolicy::ExternalSandbox { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn token_usage(&self) -> codex_core::protocol::TokenUsage {
|
||||
self.chat_widget.token_usage()
|
||||
}
|
||||
|
||||
@@ -85,6 +85,12 @@ pub(crate) enum AppEvent {
|
||||
effort: Option<ReasoningEffort>,
|
||||
},
|
||||
|
||||
/// Persist the selected approval policy and sandbox policy to the appropriate config.
|
||||
PersistApprovalSelection {
|
||||
approval: AskForApproval,
|
||||
sandbox: SandboxPolicy,
|
||||
},
|
||||
|
||||
/// Open the reasoning selection popup after picking a model.
|
||||
OpenReasoningPopup {
|
||||
model: ModelPreset,
|
||||
|
||||
@@ -2763,6 +2763,7 @@ impl ChatWidget {
|
||||
.notices
|
||||
.hide_full_access_warning
|
||||
.unwrap_or(false);
|
||||
let persist_preset = self.should_persist_approval_preset(&preset);
|
||||
let actions: Vec<SelectionAction> = if requires_confirmation {
|
||||
let preset_clone = preset.clone();
|
||||
vec![Box::new(move |tx| {
|
||||
@@ -2806,15 +2807,27 @@ impl ChatWidget {
|
||||
});
|
||||
})]
|
||||
} else {
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
persist_preset,
|
||||
)
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
persist_preset,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
persist_preset,
|
||||
)
|
||||
};
|
||||
items.push(SelectionItem {
|
||||
name,
|
||||
@@ -2848,22 +2861,40 @@ impl ChatWidget {
|
||||
fn approval_preset_actions(
|
||||
approval: AskForApproval,
|
||||
sandbox: SandboxPolicy,
|
||||
persist: bool,
|
||||
) -> Vec<SelectionAction> {
|
||||
vec![Box::new(move |tx| {
|
||||
let sandbox_clone = sandbox.clone();
|
||||
let sandbox_for_update = sandbox.clone();
|
||||
let sandbox_for_persist = sandbox.clone();
|
||||
tx.send(AppEvent::CodexOp(Op::OverrideTurnContext {
|
||||
cwd: None,
|
||||
approval_policy: Some(approval),
|
||||
sandbox_policy: Some(sandbox_clone.clone()),
|
||||
sandbox_policy: Some(sandbox_for_update.clone()),
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
}));
|
||||
tx.send(AppEvent::UpdateAskForApprovalPolicy(approval));
|
||||
tx.send(AppEvent::UpdateSandboxPolicy(sandbox_clone));
|
||||
tx.send(AppEvent::UpdateSandboxPolicy(sandbox_for_update));
|
||||
if persist {
|
||||
tx.send(AppEvent::PersistApprovalSelection {
|
||||
approval,
|
||||
sandbox: sandbox_for_persist,
|
||||
});
|
||||
}
|
||||
})]
|
||||
}
|
||||
|
||||
fn should_persist_approval_preset(&self, preset: &ApprovalPreset) -> bool {
|
||||
if preset.id != "full-access" {
|
||||
return true;
|
||||
}
|
||||
self.config
|
||||
.notices
|
||||
.hide_full_access_warning
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn preset_matches_current(
|
||||
current_approval: AskForApproval,
|
||||
current_sandbox: &SandboxPolicy,
|
||||
@@ -2933,12 +2964,10 @@ impl ChatWidget {
|
||||
));
|
||||
let header = ColumnRenderable::with(header_children);
|
||||
|
||||
let mut accept_actions = Self::approval_preset_actions(approval, sandbox.clone());
|
||||
accept_actions.push(Box::new(|tx| {
|
||||
tx.send(AppEvent::UpdateFullAccessWarningAcknowledged(true));
|
||||
}));
|
||||
let accept_actions = Self::approval_preset_actions(approval, sandbox.clone(), false);
|
||||
|
||||
let mut accept_and_remember_actions = Self::approval_preset_actions(approval, sandbox);
|
||||
let mut accept_and_remember_actions =
|
||||
Self::approval_preset_actions(approval, sandbox, true);
|
||||
accept_and_remember_actions.push(Box::new(|tx| {
|
||||
tx.send(AppEvent::UpdateFullAccessWarningAcknowledged(true));
|
||||
tx.send(AppEvent::PersistFullAccessWarningAcknowledged);
|
||||
@@ -3043,8 +3072,15 @@ impl ChatWidget {
|
||||
tx.send(AppEvent::SkipNextWorldWritableScan);
|
||||
}));
|
||||
}
|
||||
let persist_approval = preset
|
||||
.as_ref()
|
||||
.is_some_and(|preset| self.should_persist_approval_preset(preset));
|
||||
if let (Some(approval), Some(sandbox)) = (approval, sandbox.clone()) {
|
||||
accept_actions.extend(Self::approval_preset_actions(approval, sandbox));
|
||||
accept_actions.extend(Self::approval_preset_actions(
|
||||
approval,
|
||||
sandbox,
|
||||
persist_approval,
|
||||
));
|
||||
}
|
||||
|
||||
let mut accept_and_remember_actions: Vec<SelectionAction> = Vec::new();
|
||||
@@ -3053,7 +3089,11 @@ impl ChatWidget {
|
||||
tx.send(AppEvent::PersistWorldWritableWarningAcknowledged);
|
||||
}));
|
||||
if let (Some(approval), Some(sandbox)) = (approval, sandbox) {
|
||||
accept_and_remember_actions.extend(Self::approval_preset_actions(approval, sandbox));
|
||||
accept_and_remember_actions.extend(Self::approval_preset_actions(
|
||||
approval,
|
||||
sandbox,
|
||||
persist_approval,
|
||||
));
|
||||
}
|
||||
|
||||
let items = vec![
|
||||
@@ -3158,7 +3198,11 @@ impl ChatWidget {
|
||||
.iter()
|
||||
.find(|preset| preset.id == "read-only")
|
||||
.map(|preset| {
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
preset.id != "full-access",
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
};
|
||||
@@ -3238,7 +3282,11 @@ impl ChatWidget {
|
||||
.iter()
|
||||
.find(|preset| preset.id == "read-only")
|
||||
.map(|preset| {
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
preset.id != "full-access",
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user