diff --git a/README.md b/README.md index 2a1a804..2cd0f77 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ curl -fsSL https://raw.githubusercontent.com/antomanc/agbox/main/install.sh | ba [![CI](https://github.com/antomanc/agbox/actions/workflows/ci.yml/badge.svg)](https://github.com/antomanc/agbox/actions) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +

agbox doctor: every check passing on a mini PC

+ ## Why Running agents on a headless Linux box usually means a pile of small failures: diff --git a/bin/agbox b/bin/agbox index 1f747c7..52ed6ed 100755 --- a/bin/agbox +++ b/bin/agbox @@ -108,8 +108,12 @@ _SUDO_KEEPALIVE_PID='' preflight() { if ! is_root; then has_cmd sudo || die "sudo is not installed: run agbox as root (it will create '$AGBOX_USER' for you)" - log_info "agbox needs sudo; you may be asked for your password once" - sudo -v || die "sudo authentication failed" + # Try non-interactively first: `sudo -v` asks for a password whenever any sudoers + # rule needs one (Debian's %sudo does), even if a NOPASSWD rule also matches. + if ! sudo -n true 2>/dev/null; then + log_info "agbox needs sudo; you may be asked for your password once" + sudo -v || die "sudo authentication failed" + fi # Keep the sudo timestamp fresh for long runs; stops when agbox exits. (while kill -0 "$$" 2>/dev/null; do sudo -n true 2>/dev/null || true diff --git a/docs/assets/demo.gif b/docs/assets/demo.gif new file mode 100644 index 0000000..0fe3f8c Binary files /dev/null and b/docs/assets/demo.gif differ diff --git a/docs/assets/demo.tape b/docs/assets/demo.tape new file mode 100644 index 0000000..bace503 --- /dev/null +++ b/docs/assets/demo.tape @@ -0,0 +1,31 @@ +# Hero GIF for the README. Regenerate with: vhs docs/assets/demo.tape +# Needs vhs, ttyd and ffmpeg, and a machine already set up with agbox. + +Output docs/assets/demo.gif + +Set Shell bash +Set FontFamily "JetBrains Mono" +Set FontSize 15 +Set LineHeight 1.15 +Set Width 820 +Set Height 1200 +Set Padding 24 +Set Theme "Catppuccin Mocha" +Set TypingSpeed 60ms +Set WindowBar Colorful + +# Tidy prompt; skip the SSH check (this box uses Tailscale SSH) and hide the tailnet name. +Hide +Type `export PS1='\[\e[32m\]antomanc@home-server\[\e[0m\]:\[\e[34m\]~\[\e[0m\]$ '` +Enter +Type `agbox() { "$PWD/bin/agbox" "$@" --skip ssh | sed -E 's/\.[a-z0-9-]+\.ts\.net/.tailnet.ts.net/'; }` +Enter +Type "clear" +Enter +Show + +Sleep 800ms +Type "agbox doctor" +Sleep 400ms +Enter +Sleep 5s diff --git a/docs/assets/doctor.png b/docs/assets/doctor.png new file mode 100644 index 0000000..391a811 Binary files /dev/null and b/docs/assets/doctor.png differ diff --git a/modules/system.sh b/modules/system.sh index 0f66b34..27e5073 100644 --- a/modules/system.sh +++ b/modules/system.sh @@ -4,7 +4,7 @@ SYSTEM_PACKAGES=( sudo curl ca-certificates gnupg git build-essential pkg-config jq ripgrep fd-find tmux unzip zip htop rsync bubblewrap socat - lsb-release dnsutils file less nano + lsb-release bind9-dnsutils file less nano ) system::describe() { echo "base packages, upgrades, hostname, timezone, zram, auto-updates"; } diff --git a/tests/cli.bats b/tests/cli.bats index 7e45ccd..fc65761 100644 --- a/tests/cli.bats +++ b/tests/cli.bats @@ -86,3 +86,15 @@ setup() { [ "$(grep -c 'Apply?' <<<"$output")" -eq 2 ] ! id agbox-dryrun-user 2>/dev/null } + +@test "setup works with passwordless sudo when sudo -v would prompt" { + [ "$EUID" -ne 0 ] || skip "root never calls sudo" + # Fake sudo: NOPASSWD for commands, but -v demands a password (Debian's %sudo rule). + mkdir -p "$BATS_TEST_TMPDIR/bin" + printf '#!/bin/sh\n[ "$1" = -v ] && exit 1\n[ "$1" = -n ] && shift\n[ "$1" = -- ] && shift\nexec "$@"\n' \ + >"$BATS_TEST_TMPDIR/bin/sudo" + chmod +x "$BATS_TEST_TMPDIR/bin/sudo" + PATH="$BATS_TEST_TMPDIR/bin:$PATH" run "$AGBOX" setup --dry-run --yes --set USER=agbox-dryrun-user --only system + [ "$status" -eq 0 ] + [[ "$output" != *"sudo authentication failed"* ]] +}