Files
coredesktop/sandbox/setup.sh
T
2026-10-28 06:26:57 -06:00

96 lines
3.9 KiB
Bash

#!/bin/bash
#
# Purpose : Set up sandboxed AI agents (see sandbox/agent): install or update
# Anthropic's sandbox runtime (srt) and Hermes Agent, and turn on
# Claude Code's built-in sandbox. Run by install.sh and on every
# update (updates.d/72-sandbox.sh).
COREDESKTOP="$HOME/.local/share/coredesktop"
STATUS=0
# npm's global prefix is under /usr, so install to ~/.local; srt lands in ~/.local/bin
echo "Installing the sandbox runtime (srt)..."
npm install --global --prefix "$HOME/.local" --no-fund --no-audit @anthropic-ai/sandbox-runtime || STATUS=1
# --non-interactive skips Hermes' setup questions and its background gateway
# service; `configure hermes` chooses a model provider afterwards
if [ ! -x "$HOME/.local/bin/hermes" ]; then
echo "Installing Hermes Agent..."
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash -s -- --non-interactive || STATUS=1
fi
# Claude Code runs every command it starts in its own sandbox, also built on
# bubblewrap and socat; it can't run inside srt because bubblewrap can't be
# nested. Anything already in the sandbox section of your settings wins over
# these defaults.
CLAUDE_SETTINGS="$HOME/.claude/settings.json"
if [ -f "$CLAUDE_SETTINGS" ]; then
domains="$(jq '.network.allowedDomains' "$COREDESKTOP/sandbox/config/base.json")"
tmp="$(mktemp "$CLAUDE_SETTINGS.XXXXXX")"
if jq --argjson domains "$domains" \
'.sandbox = ({enabled: true, network: {allowedDomains: $domains}} * (.sandbox // {}))' \
"$CLAUDE_SETTINGS" > "$tmp"; then
if cmp -s "$tmp" "$CLAUDE_SETTINGS"; then
rm -f "$tmp"
else
echo "Turning on Claude Code's sandbox..."
mv -f "$tmp" "$CLAUDE_SETTINGS"
fi
else
echo "Couldn't read $CLAUDE_SETTINGS; Claude Code's sandbox was not turned on."
rm -f "$tmp"
STATUS=1
fi
fi
# Cline Desktop is opened from the app menu, so an entry of the same name in
# ~/.local/share/applications, which wins over the package's, runs it in the
# sandbox. It's removed when the package is.
CLINE_DESKTOP="/usr/share/applications/cline-desktop.desktop"
CLINE_ENTRY="$HOME/.local/share/applications/cline-desktop.desktop"
if [ -f "$CLINE_DESKTOP" ]; then
tmp="$(mktemp)"
sed "s#^Exec=.*#Exec=$COREDESKTOP/sandbox/bin/cline-desktop %U#" "$CLINE_DESKTOP" > "$tmp"
if cmp -s "$tmp" "$CLINE_ENTRY"; then
rm -f "$tmp"
else
echo "Running Cline Desktop in the sandbox..."
mkdir -p "$(dirname "$CLINE_ENTRY")" && mv -f "$tmp" "$CLINE_ENTRY" && chmod 644 "$CLINE_ENTRY" || STATUS=1
fi
elif [ -f "$CLINE_ENTRY" ]; then
rm -f "$CLINE_ENTRY"
fi
# Hermes' gateway acts on messages from outside this machine, so it runs in
# the sandbox too: a systemd drop-in starts it through sandbox/bin/hermes. It
# also needs to reach the Matrix server in Hermes' settings, which differs on
# each machine; for other messaging services, add their domains to
# ~/.config/coredesktop/sandbox/hermes.json.
GATEWAY_UNIT="$HOME/.config/systemd/user/hermes-gateway.service"
if [ -f "$GATEWAY_UNIT" ]; then
if [ -x "$HOME/.local/bin/srt" ]; then
homeserver="$(sed -nE "s#^MATRIX_HOMESERVER=[\"']?https?://([^/:\"']+).*#\1#p" "$HOME/.hermes/.env" 2> /dev/null | head -n 1)"
dropin="$GATEWAY_UNIT.d/coredesktop-sandbox.conf"
tmp="$(mktemp)"
cat > "$tmp" << EOF
# Written by CoreDesktop (sandbox/setup.sh): run Hermes' gateway in the sandbox
[Service]
Environment="COREDESKTOP_SANDBOX_DOMAINS=$homeserver"
ExecStart=
ExecStart=%h/.local/share/coredesktop/sandbox/bin/hermes gateway run
EOF
if cmp -s "$tmp" "$dropin"; then
rm -f "$tmp"
else
echo "Running Hermes' gateway in the sandbox..."
mkdir -p "$(dirname "$dropin")" && mv -f "$tmp" "$dropin" && chmod 644 "$dropin" \
&& systemctl --user daemon-reload || STATUS=1
fi
else
echo "Hermes' gateway isn't sandboxed yet: the sandbox (srt) isn't installed."
STATUS=1
fi
fi
exit $STATUS