mirror of
https://github.com/openai/codex.git
synced 2026-04-26 15:45:02 +00:00
improvements, danger widget
This commit is contained in:
202
codex-rs/tui/src/command_utils.rs
Normal file
202
codex-rs/tui/src/command_utils.rs
Normal file
@@ -0,0 +1,202 @@
|
||||
//! Small shared helpers for slash-command argument parsing and execution-mode display.
|
||||
|
||||
use codex_core::protocol::AskForApproval;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
|
||||
/// Canonical execution presets that combine approval policy and sandbox policy.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ExecutionPreset {
|
||||
/// never prompt; read-only FS
|
||||
ReadOnly,
|
||||
/// ask to retry outside sandbox only on sandbox breach; read-only FS
|
||||
Untrusted,
|
||||
/// auto within workspace sandbox; ask to retry outside on breach
|
||||
Auto,
|
||||
/// DANGEROUS: disables sandbox and approvals entirely.
|
||||
FullYolo,
|
||||
}
|
||||
|
||||
impl ExecutionPreset {
|
||||
pub fn label(self) -> &'static str {
|
||||
match self {
|
||||
ExecutionPreset::ReadOnly => "Read only",
|
||||
ExecutionPreset::Untrusted => "Untrusted",
|
||||
ExecutionPreset::Auto => "Auto",
|
||||
ExecutionPreset::FullYolo => "Full yolo",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn description(self) -> &'static str {
|
||||
match self {
|
||||
ExecutionPreset::ReadOnly => {
|
||||
"never prompt; read-only filesystem (flags: --ask-for-approval never --sandbox read-only)"
|
||||
}
|
||||
ExecutionPreset::Untrusted => {
|
||||
"ask to retry outside sandbox only on sandbox breach; read-only (flags: --ask-for-approval on-failure --sandbox read-only)"
|
||||
}
|
||||
ExecutionPreset::Auto => {
|
||||
"auto in workspace sandbox; ask to retry outside sandbox on breach (flags: --ask-for-approval on-failure --sandbox workspace-write)"
|
||||
}
|
||||
ExecutionPreset::FullYolo => {
|
||||
"DANGEROUS: disables sandbox and approvals; the agent can run any commands with full system access (flags: --dangerously-bypass-approvals-and-sandbox)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Canonical CLI flags string for the preset.
|
||||
pub fn cli_flags(self) -> &'static str {
|
||||
match self {
|
||||
ExecutionPreset::ReadOnly => "--ask-for-approval never --sandbox read-only",
|
||||
ExecutionPreset::Untrusted => "--ask-for-approval on-failure --sandbox read-only",
|
||||
ExecutionPreset::Auto => "--ask-for-approval on-failure --sandbox workspace-write",
|
||||
ExecutionPreset::FullYolo => "--dangerously-bypass-approvals-and-sandbox",
|
||||
}
|
||||
}
|
||||
|
||||
/// Mapping from preset to policies.
|
||||
pub fn to_policies(self) -> (AskForApproval, SandboxPolicy) {
|
||||
match self {
|
||||
ExecutionPreset::ReadOnly => (AskForApproval::Never, SandboxPolicy::ReadOnly),
|
||||
ExecutionPreset::Untrusted => (AskForApproval::OnFailure, SandboxPolicy::ReadOnly),
|
||||
ExecutionPreset::Auto => (
|
||||
AskForApproval::OnFailure,
|
||||
SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
network_access: false,
|
||||
include_default_writable_roots: true,
|
||||
},
|
||||
),
|
||||
ExecutionPreset::FullYolo => (AskForApproval::Never, SandboxPolicy::DangerFullAccess),
|
||||
}
|
||||
}
|
||||
|
||||
/// Mapping from policies to a known preset.
|
||||
pub fn from_policies(
|
||||
approval: AskForApproval,
|
||||
sandbox: &SandboxPolicy,
|
||||
) -> Option<ExecutionPreset> {
|
||||
match (approval, sandbox) {
|
||||
(AskForApproval::Never, SandboxPolicy::ReadOnly) => Some(ExecutionPreset::ReadOnly),
|
||||
(AskForApproval::OnFailure, SandboxPolicy::ReadOnly) => {
|
||||
Some(ExecutionPreset::Untrusted)
|
||||
}
|
||||
(AskForApproval::OnFailure, SandboxPolicy::WorkspaceWrite { .. }) => {
|
||||
Some(ExecutionPreset::Auto)
|
||||
}
|
||||
(AskForApproval::Never, SandboxPolicy::DangerFullAccess)
|
||||
| (AskForApproval::OnFailure, SandboxPolicy::DangerFullAccess) => {
|
||||
Some(ExecutionPreset::FullYolo)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse one of the canonical tokens: read-only | untrusted | auto.
|
||||
pub fn parse_token(s: &str) -> Option<ExecutionPreset> {
|
||||
let t = s.trim().to_ascii_lowercase();
|
||||
let t = t.replace(' ', "-");
|
||||
match t.as_str() {
|
||||
"read-only" => Some(ExecutionPreset::ReadOnly),
|
||||
"untrusted" => Some(ExecutionPreset::Untrusted),
|
||||
"auto" => Some(ExecutionPreset::Auto),
|
||||
"full-yolo" => Some(ExecutionPreset::FullYolo),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Strip a single pair of surrounding quotes from the provided string if present.
|
||||
/// Supports straight and common curly quotes: '…', "…", ‘…’, “…”.
|
||||
pub fn strip_surrounding_quotes(s: &str) -> &str {
|
||||
// Opening/closing pairs (note curly quotes differ on each side)
|
||||
const QUOTE_PAIRS: &[(char, char)] = &[('\"', '\"'), ('\'', '\''), ('“', '”'), ('‘', '’')];
|
||||
|
||||
let t = s.trim();
|
||||
if t.len() < 2 {
|
||||
return t;
|
||||
}
|
||||
|
||||
for &(open, close) in QUOTE_PAIRS {
|
||||
if t.starts_with(open) && t.ends_with(close) {
|
||||
let start = open.len_utf8();
|
||||
let end = t.len() - close.len_utf8();
|
||||
return &t[start..end];
|
||||
}
|
||||
}
|
||||
|
||||
t
|
||||
}
|
||||
|
||||
/// Normalize a free-form token: trim whitespace and remove a single pair of surrounding quotes.
|
||||
pub fn normalize_token(s: &str) -> String {
|
||||
strip_surrounding_quotes(s).trim().to_string()
|
||||
}
|
||||
|
||||
/// Map an (approval, sandbox) pair to a concise preset label used in the UI.
|
||||
pub fn execution_mode_label(approval: AskForApproval, sandbox: &SandboxPolicy) -> &'static str {
|
||||
ExecutionPreset::from_policies(approval, sandbox)
|
||||
.map(|p| p.label())
|
||||
.unwrap_or("Custom")
|
||||
}
|
||||
|
||||
/// Describe the current execution preset including CLI flag equivalents.
|
||||
pub fn execution_mode_description(
|
||||
approval: AskForApproval,
|
||||
sandbox: &SandboxPolicy,
|
||||
) -> &'static str {
|
||||
ExecutionPreset::from_policies(approval, sandbox)
|
||||
.map(|p| p.description())
|
||||
.unwrap_or("custom combination")
|
||||
}
|
||||
|
||||
/// Parse a free-form token to an execution preset (approval+sandbox).
|
||||
pub fn parse_execution_mode_token(s: &str) -> Option<(AskForApproval, SandboxPolicy)> {
|
||||
ExecutionPreset::parse_token(s).map(|p| p.to_policies())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn strip_quotes_variants() {
|
||||
assert_eq!(strip_surrounding_quotes("\"o3\""), "o3");
|
||||
assert_eq!(strip_surrounding_quotes("'o3'"), "o3");
|
||||
assert_eq!(strip_surrounding_quotes("“o3”"), "o3");
|
||||
assert_eq!(strip_surrounding_quotes("‘o3’"), "o3");
|
||||
assert_eq!(strip_surrounding_quotes("o3"), "o3");
|
||||
assert_eq!(strip_surrounding_quotes(" o3 "), "o3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_execution_mode_aliases() {
|
||||
use codex_core::protocol::AskForApproval;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
let parse = parse_execution_mode_token;
|
||||
assert!(matches!(
|
||||
parse("auto").unwrap(),
|
||||
(
|
||||
AskForApproval::OnFailure,
|
||||
SandboxPolicy::WorkspaceWrite { .. }
|
||||
)
|
||||
));
|
||||
assert_eq!(
|
||||
parse("untrusted"),
|
||||
Some((AskForApproval::OnFailure, SandboxPolicy::ReadOnly))
|
||||
);
|
||||
assert_eq!(
|
||||
parse("read-only"),
|
||||
Some((AskForApproval::Never, SandboxPolicy::ReadOnly))
|
||||
);
|
||||
assert_eq!(
|
||||
parse("full-yolo"),
|
||||
Some((AskForApproval::Never, SandboxPolicy::DangerFullAccess))
|
||||
);
|
||||
assert_eq!(
|
||||
parse("Full Yolo"),
|
||||
Some((AskForApproval::Never, SandboxPolicy::DangerFullAccess))
|
||||
);
|
||||
assert_eq!(parse("unknown"), None);
|
||||
assert!(parse(" AUTO ").is_some());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user