mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
159 lines
4.7 KiB
Rust
159 lines
4.7 KiB
Rust
/*
|
|
Runtime: shell
|
|
|
|
Executes shell requests under the orchestrator: asks for approval when needed,
|
|
builds a CommandSpec, and runs it under the current SandboxAttempt.
|
|
*/
|
|
use crate::exec::ExecToolCallOutput;
|
|
use crate::sandboxing::execute_env;
|
|
use crate::tools::runtimes::build_command_spec;
|
|
use crate::tools::sandboxing::Approvable;
|
|
use crate::tools::sandboxing::ApprovalCtx;
|
|
use crate::tools::sandboxing::ApprovalRequirement;
|
|
use crate::tools::sandboxing::ProvidesSandboxRetryData;
|
|
use crate::tools::sandboxing::SandboxAttempt;
|
|
use crate::tools::sandboxing::SandboxRetryData;
|
|
use crate::tools::sandboxing::Sandboxable;
|
|
use crate::tools::sandboxing::SandboxablePreference;
|
|
use crate::tools::sandboxing::ToolCtx;
|
|
use crate::tools::sandboxing::ToolError;
|
|
use crate::tools::sandboxing::ToolRuntime;
|
|
use crate::tools::sandboxing::with_cached_approval;
|
|
use codex_protocol::protocol::ReviewDecision;
|
|
use futures::future::BoxFuture;
|
|
use std::path::PathBuf;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ShellRequest {
|
|
pub command: Vec<String>,
|
|
pub cwd: PathBuf,
|
|
pub timeout_ms: Option<u64>,
|
|
pub env: std::collections::HashMap<String, String>,
|
|
pub with_escalated_permissions: Option<bool>,
|
|
pub justification: Option<String>,
|
|
pub approval_requirement: ApprovalRequirement,
|
|
pub approval_allow_prefix: Option<Vec<String>>,
|
|
}
|
|
|
|
impl ProvidesSandboxRetryData for ShellRequest {
|
|
fn sandbox_retry_data(&self) -> Option<SandboxRetryData> {
|
|
Some(SandboxRetryData {
|
|
command: self.command.clone(),
|
|
cwd: self.cwd.clone(),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct ShellRuntime;
|
|
|
|
#[derive(serde::Serialize, Clone, Debug, Eq, PartialEq, Hash)]
|
|
pub(crate) struct ApprovalKey {
|
|
command: Vec<String>,
|
|
cwd: PathBuf,
|
|
escalated: bool,
|
|
}
|
|
|
|
impl ShellRuntime {
|
|
pub fn new() -> Self {
|
|
Self
|
|
}
|
|
|
|
fn stdout_stream(ctx: &ToolCtx<'_>) -> Option<crate::exec::StdoutStream> {
|
|
Some(crate::exec::StdoutStream {
|
|
sub_id: ctx.turn.sub_id.clone(),
|
|
call_id: ctx.call_id.clone(),
|
|
tx_event: ctx.session.get_tx_event(),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Sandboxable for ShellRuntime {
|
|
fn sandbox_preference(&self) -> SandboxablePreference {
|
|
SandboxablePreference::Auto
|
|
}
|
|
fn escalate_on_failure(&self) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
impl Approvable<ShellRequest> for ShellRuntime {
|
|
type ApprovalKey = ApprovalKey;
|
|
|
|
fn approval_key(&self, req: &ShellRequest) -> Self::ApprovalKey {
|
|
ApprovalKey {
|
|
command: req.command.clone(),
|
|
cwd: req.cwd.clone(),
|
|
escalated: req.with_escalated_permissions.unwrap_or(false),
|
|
}
|
|
}
|
|
|
|
fn start_approval_async<'a>(
|
|
&'a mut self,
|
|
req: &'a ShellRequest,
|
|
ctx: ApprovalCtx<'a>,
|
|
) -> BoxFuture<'a, ReviewDecision> {
|
|
let key = self.approval_key(req);
|
|
let command = req.command.clone();
|
|
let cwd = req.cwd.clone();
|
|
let reason = ctx
|
|
.retry_reason
|
|
.clone()
|
|
.or_else(|| req.justification.clone());
|
|
let risk = ctx.risk.clone();
|
|
let allow_prefix = req.approval_allow_prefix.clone();
|
|
let session = ctx.session;
|
|
let turn = ctx.turn;
|
|
let call_id = ctx.call_id.to_string();
|
|
Box::pin(async move {
|
|
with_cached_approval(&session.services, key, move || async move {
|
|
session
|
|
.request_command_approval(
|
|
turn,
|
|
call_id,
|
|
command,
|
|
cwd,
|
|
reason,
|
|
risk,
|
|
allow_prefix,
|
|
)
|
|
.await
|
|
})
|
|
.await
|
|
})
|
|
}
|
|
|
|
fn approval_requirement(&self, req: &ShellRequest) -> Option<ApprovalRequirement> {
|
|
Some(req.approval_requirement.clone())
|
|
}
|
|
|
|
fn wants_escalated_first_attempt(&self, req: &ShellRequest) -> bool {
|
|
req.with_escalated_permissions.unwrap_or(false)
|
|
}
|
|
}
|
|
|
|
impl ToolRuntime<ShellRequest, ExecToolCallOutput> for ShellRuntime {
|
|
async fn run(
|
|
&mut self,
|
|
req: &ShellRequest,
|
|
attempt: &SandboxAttempt<'_>,
|
|
ctx: &ToolCtx<'_>,
|
|
) -> Result<ExecToolCallOutput, ToolError> {
|
|
let spec = build_command_spec(
|
|
&req.command,
|
|
&req.cwd,
|
|
&req.env,
|
|
req.timeout_ms,
|
|
req.with_escalated_permissions,
|
|
req.justification.clone(),
|
|
)?;
|
|
let env = attempt
|
|
.env_for(&spec)
|
|
.map_err(|err| ToolError::Codex(err.into()))?;
|
|
let out = execute_env(&env, attempt.policy, Self::stdout_stream(ctx))
|
|
.await
|
|
.map_err(ToolError::Codex)?;
|
|
Ok(out)
|
|
}
|
|
}
|