Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:

- name: ShellCheck
run: |
find bin/ setup.sh start.sh -type f \( -name '*.sh' -o -name 'baudbot-safe-bash' -o -name 'baudbot-docker' \) \
find bin/ setup.sh start.sh install.sh -type f \( -name '*.sh' -o -name 'baudbot-safe-bash' -o -name 'baudbot-docker' -o -name 'baudbot' \) \
| xargs shellcheck -s bash -S warning

test:
Expand Down
215 changes: 215 additions & 0 deletions bin/baudbot
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
#!/bin/bash
# Baudbot CLI dispatcher
# Routes subcommands to internal scripts. Installed as /usr/local/bin/baudbot.
#
# Usage: baudbot <command> [options]

set -euo pipefail

# Find the baudbot source root. Resolution order:
# 1. BAUDBOT_ROOT env var (explicit override)
# 2. Resolve from this script's real location (follow symlinks, then ../)
if [ -z "${BAUDBOT_ROOT:-}" ]; then
SCRIPT="$0"
# Follow symlinks to find the real script location
while [ -L "$SCRIPT" ]; do
DIR="$(cd "$(dirname "$SCRIPT")" && pwd)"
SCRIPT="$(readlink "$SCRIPT")"
# Handle relative symlinks
[[ "$SCRIPT" != /* ]] && SCRIPT="$DIR/$SCRIPT"
done
BAUDBOT_ROOT="$(cd "$(dirname "$SCRIPT")/.." && pwd)"
fi

# Colors (disabled if not a terminal)
if [ -t 1 ]; then
BOLD='\033[1m'
RESET='\033[0m'
else
BOLD='' RESET=''
fi

version() {
if [ -f "$BAUDBOT_ROOT/package.json" ]; then
grep '"version"' "$BAUDBOT_ROOT/package.json" | head -1 | sed 's/.*: *"\(.*\)".*/\1/'
else
echo "unknown"
fi
}

usage() {
echo -e "${BOLD}baudbot${RESET} — hardened infrastructure for always-on AI agents"
echo ""
echo -e "${BOLD}Usage:${RESET} baudbot <command> [options]"
echo ""
echo -e "${BOLD}Lifecycle:${RESET}"
echo " start Start the agent (systemd, or --direct for foreground)"
echo " stop Stop the agent"
echo " restart Restart the agent"
echo " status Show agent status"
echo " logs Tail agent logs"
echo ""
echo -e "${BOLD}Setup:${RESET}"
echo " setup One-time system setup (user, deps, firewall, systemd)"
echo " config Interactive secrets and config setup"
echo " deploy Deploy source + config to agent runtime"
echo ""
echo -e "${BOLD}Operations:${RESET}"
echo " doctor Health check (perms, firewall, secrets, deps)"
echo " audit Security posture audit"
echo " test Run test suite"
echo " update Pull latest and redeploy"
echo " uninstall Remove everything"
echo ""
echo -e "${BOLD}Options:${RESET}"
echo " --version Show version"
echo " --help, -h Show this help"
}

require_root() {
if [ "$(id -u)" -ne 0 ]; then
echo "❌ baudbot $1 requires root. Run: sudo baudbot $1"
exit 1
fi
}

# Detect systemd
has_systemd() {
command -v systemctl &>/dev/null && [ -d /run/systemd/system ]
}

case "${1:-}" in
start)
shift
if [ "${1:-}" = "--direct" ]; then
# Foreground mode: run start.sh directly (for dev/CI/debugging)
shift
require_root "start --direct"
exec sudo -u baudbot_agent "$BAUDBOT_ROOT/start.sh" "$@"
Comment thread
sentry[bot] marked this conversation as resolved.
else
require_root "start"
if has_systemd; then
exec systemctl start baudbot "$@"
else
echo "systemd not available. Use: baudbot start --direct"
exit 1
fi
fi
;;

stop)
shift
require_root "stop"
if has_systemd; then
exec systemctl stop baudbot "$@"
else
echo "systemd not available. Kill the agent process manually."
exit 1
fi
;;

restart)
shift
require_root "restart"
if has_systemd; then
exec systemctl restart baudbot "$@"
else
echo "systemd not available."
exit 1
fi
;;

status)
shift
if has_systemd && systemctl is-enabled baudbot &>/dev/null 2>&1; then
exec systemctl status baudbot "$@"
else
# Fallback: check if baudbot_agent has pi running
if pgrep -u baudbot_agent -f "pi --session-control" &>/dev/null; then
echo "baudbot is running (no systemd unit)"
pgrep -u baudbot_agent -af "pi --session-control"
else
echo "baudbot is not running"
fi
fi
;;

logs)
shift
if has_systemd && systemctl is-enabled baudbot &>/dev/null 2>&1; then
exec journalctl -u baudbot -f "$@"
else
echo "No systemd unit. Check tmux sessions:"
echo " sudo -u baudbot_agent tmux ls"
fi
;;

setup)
shift
require_root "setup"
exec "$BAUDBOT_ROOT/setup.sh" "$@"
;;

config)
shift
# config writes to ~/.baudbot/ on the calling user
# For now, point to install.sh's config section
echo "Config management is not yet extracted into a standalone command."
echo "Use: sudo $BAUDBOT_ROOT/install.sh"
exit 1
;;

deploy)
shift
require_root "deploy"
exec "$BAUDBOT_ROOT/bin/deploy.sh" "$@"
;;

audit)
shift
exec "$BAUDBOT_ROOT/bin/security-audit.sh" "$@"
;;

test)
shift
exec "$BAUDBOT_ROOT/bin/test.sh" "$@"
;;

update)
shift
# For now: git pull + deploy
require_root "update"
echo "=== Pulling latest ==="
cd "$BAUDBOT_ROOT"
git pull origin main
echo ""
echo "=== Deploying ==="
exec "$BAUDBOT_ROOT/bin/deploy.sh" "$@"
;;

uninstall)
shift
require_root "uninstall"
exec "$BAUDBOT_ROOT/bin/uninstall.sh" "$@"
;;

doctor)
shift
exec "$BAUDBOT_ROOT/bin/doctor.sh" "$@"
;;

--version|-v)
echo "baudbot $(version)"
;;

--help|-h|"")
usage
;;

*)
echo "Unknown command: $1"
echo ""
usage
exit 1
;;
esac
36 changes: 36 additions & 0 deletions bin/baudbot.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[Unit]
Description=Baudbot AI Agent
After=network.target
Documentation=https://github.com/modem-dev/baudbot

[Service]
Type=simple
User=baudbot_agent
Group=baudbot_agent
WorkingDirectory=/home/baudbot_agent

# Pre-start: lock down perms and scrub secrets from old logs
ExecStartPre=/home/baudbot_agent/runtime/bin/harden-permissions.sh
ExecStartPre=/bin/bash -c '/home/baudbot_agent/runtime/bin/redact-logs.sh 2>/dev/null || true'

# Main process
ExecStart=/home/baudbot_agent/runtime/start.sh

# Restart on failure, but not on clean exit (agent chose to stop)
Restart=on-failure
RestartSec=10

# Environment
Environment=PATH=/home/baudbot_agent/.varlock/bin:/home/baudbot_agent/opt/node-v22.14.0-linux-x64/bin:/usr/local/bin:/usr/bin:/bin
Environment=HOME=/home/baudbot_agent

# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=tmpfs
BindPaths=/home/baudbot_agent
ReadOnlyPaths=/opt/baudbot
PrivateTmp=yes

[Install]
WantedBy=multi-user.target
5 changes: 5 additions & 0 deletions bin/ci/setup-arch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ test -d /home/baudbot_agent/.pi/agent/extensions
grep -q "ANTHROPIC_API_KEY=sk-ant-testkey" /home/baudbot_agent/.config/.env
grep -q "SLACK_BOT_TOKEN=xoxb-test" /home/baudbot_agent/.config/.env
grep -q "BAUDBOT_SOURCE_DIR=" /home/baudbot_agent/.config/.env
# CLI installed
test -L /usr/local/bin/baudbot
baudbot --version
HELP_OUT=$(baudbot --help)
echo "$HELP_OUT" | grep -q "baudbot"
echo " ✓ install.sh verification passed"

echo "=== Installing test dependencies ==="
Expand Down
5 changes: 5 additions & 0 deletions bin/ci/setup-ubuntu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ test -d /home/baudbot_agent/.pi/agent/extensions
grep -q "ANTHROPIC_API_KEY=sk-ant-testkey" /home/baudbot_agent/.config/.env
grep -q "SLACK_BOT_TOKEN=xoxb-test" /home/baudbot_agent/.config/.env
grep -q "BAUDBOT_SOURCE_DIR=" /home/baudbot_agent/.config/.env
# CLI installed
test -L /usr/local/bin/baudbot
baudbot --version
HELP_OUT=$(baudbot --help)
echo "$HELP_OUT" | grep -q "baudbot"
echo " ✓ install.sh verification passed"

echo "=== Installing test dependencies ==="
Expand Down
Loading