Cache shell snapshots in the exec server (#39756)

## What changed

- Add the `shellSnapshotV2` executor capability and an optional shell snapshot request to `ExecParams`.
- Capture and restore Unix shell state and profile exports from an in-memory, attachment-scoped cache for `bash`, `zsh`, and `sh`.
- Apply environment policies, runtime `PATH` entries, sandbox context, and live managed-proxy settings when preparing restored commands.
- Bound snapshot size, capture time, scope length, and cache capacity, and fall back to the original command when capture fails.

## Testing

- Cover local, remote, TTY, sandboxed, and supported-shell execution, plus environment filtering, proxy handling, in-memory reuse, and capture failure fallback.

GitOrigin-RevId: 624f747972c249c88c6f10f42cf0af97b75b5541
This commit is contained in:
jif
2026-08-20 17:39:06 +00:00
committed by copyberry
parent ce950dcf26
commit d0cc662b8c
20 changed files with 799 additions and 1 deletions
@@ -5,6 +5,7 @@ use crate::shell_detect::ShellType;
mod tests;
const EXCLUDED_EXPORT_VARS: &[&str] = &["PWD", "OLDPWD"];
const EXPORT_CAPTURE_MARKER: &str = "# Capture exported variables";
/// Returns the shell-native script used to capture restorable shell state.
///
@@ -20,6 +21,16 @@ pub fn snapshot_script(shell_type: ShellType) -> Option<String> {
}
}
/// Captures shell state and a separate NUL-delimited exported environment.
///
/// Keeping exports outside the restorable script lets executors apply their
/// environment policy after shell profiles have run.
pub fn snapshot_state_and_environment_script(shell_type: ShellType) -> Option<String> {
let script = snapshot_script(shell_type)?;
let (state, _) = script.split_once(EXPORT_CAPTURE_MARKER)?;
Some(format!("{state}printf '\\0'\n/usr/bin/env -0\n"))
}
fn excluded_exports_regex() -> String {
EXCLUDED_EXPORT_VARS.join("|")
}
@@ -46,6 +57,7 @@ alias_count=$(alias -L | wc -l | tr -d ' ')
print "# aliases $alias_count"
alias -L
print ''
# Capture exported variables
export_lines=$(export -p | awk '
/^(export|declare -x|typeset -x) / {
line=$0
@@ -97,6 +109,7 @@ alias_count=$(alias -p | wc -l | tr -d ' ')
echo "# aliases $alias_count"
alias -p
echo ''
# Capture exported variables
export_lines=$(
while IFS= read -r name; do
if [[ "$name" =~ ^(EXCLUDED_EXPORTS)$ ]]; then
@@ -151,6 +164,7 @@ if alias >/dev/null 2>&1; then
else
echo '# aliases 0'
fi
# Capture exported variables
if export -p >/dev/null 2>&1; then
export_lines=$(export -p | awk '
/^(export|declare -x|typeset -x) / {