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 @@ -8,7 +8,7 @@ on:

jobs:
lint:
runs-on: &default-runner blacksmith-4vcpu-ubuntu-2404
runs-on: &default-runner ubuntu-latest
steps:
- uses: actions/checkout@v4

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ concurrency:

jobs:
docs-scope:
runs-on: &default-runner blacksmith-4vcpu-ubuntu-2404
runs-on: &default-runner ubuntu-latest
outputs:
docs_only: ${{ steps.check.outputs.docs_only }}
steps:
Expand Down
1 change: 1 addition & 0 deletions CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ Set during `setup.sh` / `baudbot install` via env vars:
| Variable | Description | Default |
|----------|-------------|---------|
| `BAUDBOT_PI_VERSION` | pi package version installed for `baudbot_agent` | `0.52.12` |
| `BAUDBOT_RUNTIME_NODE_VERSION` | embedded Node.js version downloaded to `~/opt/node-v<version>-linux-x64` (with stable symlink `~/opt/node`) | `22.14.0` |
| `GIT_USER_NAME` | Git commit author name | `baudbot-agent` |
| `GIT_USER_EMAIL` | Git commit author email | `baudbot-agent@users.noreply.github.com` |

Expand Down
45 changes: 30 additions & 15 deletions bin/baudbot
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ if [ -z "${BAUDBOT_ROOT:-}" ]; then
fi
fi

RUNTIME_NODE_HELPER="$BAUDBOT_ROOT/bin/lib/runtime-node.sh"
if [ -f "$RUNTIME_NODE_HELPER" ]; then
# shellcheck source=bin/lib/runtime-node.sh
source "$RUNTIME_NODE_HELPER"
fi

json_get_string_or_empty() {
local file="$1"
local key="$2"
Expand Down Expand Up @@ -193,7 +199,7 @@ resolve_node_bin() {
return 0
fi

# 2) Common user-managed Node installs (sudo secure_path may hide these)
# 2) Embedded runtime (sudo secure_path may hide these)
local user_home=""
if [ -n "${SUDO_USER:-}" ] && [ "${SUDO_USER}" != "root" ]; then
user_home="$(resolve_user_home "$SUDO_USER" || true)"
Expand All @@ -202,20 +208,26 @@ resolve_node_bin() {
user_home="${HOME:-}"
fi

if [ -n "${RUNTIME_NODE_HELPER:-}" ] && [ -f "$RUNTIME_NODE_HELPER" ]; then
local embedded_node=""
embedded_node="$(bb_resolve_runtime_node_bin "$user_home" || true)"
if [ -n "$embedded_node" ] && [ -x "$embedded_node" ]; then
echo "$embedded_node"
return 0
fi

embedded_node="$(bb_resolve_runtime_node_bin "/home/baudbot_agent" || true)"
if [ -n "$embedded_node" ] && [ -x "$embedded_node" ]; then
echo "$embedded_node"
return 0
fi
fi

# 3) Common user-managed Node installs
local candidate=""
for candidate in \
"$user_home/.local/share/mise/shims/node" \
"$user_home/.local/bin/node" \
"$user_home/opt/node-v22.14.0-linux-x64/bin/node" \
/home/baudbot_agent/opt/node-v22.14.0-linux-x64/bin/node \
"$user_home"/opt/node-v*-linux-x64/bin/node; do
# If the glob didn't expand, skip the literal pattern.
case "$candidate" in
*\**)
continue
;;
esac

"$user_home/.local/bin/node"; do
if [ -x "$candidate" ]; then
echo "$candidate"
return 0
Expand Down Expand Up @@ -289,11 +301,14 @@ bootstrap_install() {

echo "Escalating with $escalator for system setup..."
if [ "$escalator" = "sudo" ]; then
sudo --preserve-env=BAUDBOT_PI_VERSION bash "$install_script" "$@"
sudo --preserve-env=BAUDBOT_PI_VERSION,BAUDBOT_RUNTIME_NODE_VERSION bash "$install_script" "$@"
else
# doas has no portable preserve-env flag; pass explicitly when set.
if [ -n "${BAUDBOT_PI_VERSION:-}" ]; then
doas env BAUDBOT_PI_VERSION="$BAUDBOT_PI_VERSION" bash "$install_script" "$@"
if [ -n "${BAUDBOT_PI_VERSION:-}" ] || [ -n "${BAUDBOT_RUNTIME_NODE_VERSION:-}" ]; then
doas env \
BAUDBOT_PI_VERSION="${BAUDBOT_PI_VERSION:-}" \
BAUDBOT_RUNTIME_NODE_VERSION="${BAUDBOT_RUNTIME_NODE_VERSION:-}" \
bash "$install_script" "$@"
else
doas bash "$install_script" "$@"
fi
Expand Down
2 changes: 1 addition & 1 deletion bin/baudbot.service
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ 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=PATH=/home/baudbot_agent/.varlock/bin:/home/baudbot_agent/opt/node/bin:/usr/local/bin:/usr/bin:/bin
Environment=HOME=/home/baudbot_agent

# Security hardening
Expand Down
4 changes: 2 additions & 2 deletions bin/ci/setup-arch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ echo "$HELP_OUT" | grep -q "baudbot"
# varlock installed for agent user
test -x /home/baudbot_agent/.varlock/bin/varlock
# Agent can load env (smoke test — varlock validates schema + .env)
sudo -u baudbot_agent bash -c 'export PATH="$HOME/.varlock/bin:$HOME/opt/node-v22.14.0-linux-x64/bin:$PATH" && cd ~ && varlock load --path ~/.config/'
sudo -u baudbot_agent bash -c 'export PATH="$HOME/.varlock/bin:$HOME/opt/node/bin:$PATH" && cd ~ && varlock load --path ~/.config/'
echo " ✓ bootstrap + install verification passed"

echo "=== Running CLI smoke checks ==="
bash /home/baudbot_admin/baudbot/bin/ci/smoke-cli.sh

echo "=== Installing test dependencies ==="
export PATH="/home/baudbot_agent/opt/node-v22.14.0-linux-x64/bin:$PATH"
export PATH="/home/baudbot_agent/opt/node/bin:$PATH"
cd /home/baudbot_admin/baudbot
npm install --ignore-scripts 2>&1 | tail -1
cd slack-bridge && npm install 2>&1 | tail -1
Expand Down
4 changes: 2 additions & 2 deletions bin/ci/setup-ubuntu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,14 @@ echo "$HELP_OUT" | grep -q "baudbot"
# varlock installed for agent user
test -x /home/baudbot_agent/.varlock/bin/varlock
# Agent can load env (smoke test — varlock validates schema + .env)
sudo -u baudbot_agent bash -c 'export PATH="$HOME/.varlock/bin:$HOME/opt/node-v22.14.0-linux-x64/bin:$PATH" && cd ~ && varlock load --path ~/.config/'
sudo -u baudbot_agent bash -c 'export PATH="$HOME/.varlock/bin:$HOME/opt/node/bin:$PATH" && cd ~ && varlock load --path ~/.config/'
echo " ✓ bootstrap + install verification passed"

echo "=== Running CLI smoke checks ==="
bash /home/baudbot_admin/baudbot/bin/ci/smoke-cli.sh

echo "=== Installing test dependencies ==="
export PATH="/home/baudbot_agent/opt/node-v22.14.0-linux-x64/bin:$PATH"
export PATH="/home/baudbot_agent/opt/node/bin:$PATH"
cd /home/baudbot_admin/baudbot
npm install --ignore-scripts 2>&1 | tail -1
cd slack-bridge && npm install 2>&1 | tail -1
Expand Down
9 changes: 9 additions & 0 deletions bin/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@ if [ "$DRY_RUN" -eq 0 ]; then
cp -r --no-preserve=ownership "$BAUDBOT_SRC/pi/skills" "$STAGE_DIR/skills"
cp --no-preserve=ownership "$BAUDBOT_SRC/start.sh" "$STAGE_DIR/start.sh"
mkdir -p "$STAGE_DIR/bin"
mkdir -p "$STAGE_DIR/bin/lib"
for script in harden-permissions.sh redact-logs.sh prune-session-logs.sh; do
[ -f "$BAUDBOT_SRC/bin/$script" ] && cp --no-preserve=ownership "$BAUDBOT_SRC/bin/$script" "$STAGE_DIR/bin/$script"
done
[ -f "$BAUDBOT_SRC/bin/lib/runtime-node.sh" ] && cp --no-preserve=ownership "$BAUDBOT_SRC/bin/lib/runtime-node.sh" "$STAGE_DIR/bin/lib/runtime-node.sh"
[ -f "$BAUDBOT_SRC/pi/settings.json" ] && cp --no-preserve=ownership "$BAUDBOT_SRC/pi/settings.json" "$STAGE_DIR/settings.json"
[ -f "$BAUDBOT_SRC/.env.schema" ] && cp --no-preserve=ownership "$BAUDBOT_SRC/.env.schema" "$STAGE_DIR/.env.schema"
chmod -R a+rX "$STAGE_DIR"
Expand Down Expand Up @@ -245,6 +247,7 @@ echo "Deploying runtime scripts..."

if [ "$DRY_RUN" -eq 0 ]; then
as_agent mkdir -p "$BAUDBOT_HOME/runtime/bin"
as_agent mkdir -p "$BAUDBOT_HOME/runtime/bin/lib"

for script in harden-permissions.sh redact-logs.sh prune-session-logs.sh; do
if [ -f "$STAGE_DIR/bin/$script" ]; then
Expand All @@ -254,6 +257,12 @@ if [ "$DRY_RUN" -eq 0 ]; then
fi
done

if [ -f "$STAGE_DIR/bin/lib/runtime-node.sh" ]; then
as_agent cp "$STAGE_DIR/bin/lib/runtime-node.sh" "$BAUDBOT_HOME/runtime/bin/lib/runtime-node.sh"
as_agent chmod u+r "$BAUDBOT_HOME/runtime/bin/lib/runtime-node.sh"
log "✓ bin/lib/runtime-node.sh"
fi

as_agent cp "$STAGE_DIR/start.sh" "$BAUDBOT_HOME/runtime/start.sh"
as_agent chmod u+x "$BAUDBOT_HOME/runtime/start.sh"
log "✓ start.sh"
Expand Down
24 changes: 19 additions & 5 deletions bin/doctor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ source "$SCRIPT_DIR/lib/shell-common.sh"
source "$SCRIPT_DIR/lib/paths-common.sh"
# shellcheck source=bin/lib/doctor-common.sh
source "$SCRIPT_DIR/lib/doctor-common.sh"
# shellcheck source=bin/lib/runtime-node.sh
source "$SCRIPT_DIR/lib/runtime-node.sh"
bb_enable_strict_mode
bb_init_paths

BAUDBOT_ROOT="${BAUDBOT_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"

for arg in "$@"; do
case "$arg" in
-h|--help)
Expand Down Expand Up @@ -55,21 +59,31 @@ fi
echo ""
echo "Dependencies:"

NODE_BIN="$BAUDBOT_HOME/opt/node-v22.14.0-linux-x64/bin/node"
if [ -x "$NODE_BIN" ]; then
NODE_BIN="$(bb_resolve_runtime_node_bin "$BAUDBOT_HOME" || true)"
if [ -n "$NODE_BIN" ] && [ -x "$NODE_BIN" ]; then
NODE_VER=$("$NODE_BIN" --version 2>/dev/null || echo "unknown")
pass "Node.js $NODE_VER"
pass "Node.js $NODE_VER ($NODE_BIN)"
else
fail "Node.js not found at $NODE_BIN"
NODE_BIN="$(bb_runtime_node_bin_dir "$BAUDBOT_HOME")/node"
fail "Node.js not found (expected: $NODE_BIN)"
fi

PI_BIN="$BAUDBOT_HOME/opt/node-v22.14.0-linux-x64/bin/pi"
PI_BIN="$(bb_resolve_runtime_node_bin_dir "$BAUDBOT_HOME")/pi"
if [ -x "$PI_BIN" ] || [ -L "$PI_BIN" ]; then
pass "pi is installed"
else
fail "pi not found at $PI_BIN"
fi

if [ -n "${BAUDBOT_ROOT:-}" ] && command -v rg &>/dev/null; then
NODE_PATH_DRIFT="$(rg -n --glob '!node_modules/**' --glob '!.git/**' 'node-v[0-9]+\.[0-9]+\.[0-9]+-linux-x64' "$BAUDBOT_ROOT" || true)"
if [ -n "$NODE_PATH_DRIFT" ]; then
fail "hardcoded versioned Node paths found (run test: bin/runtime-node-paths.test.sh)"
else
pass "runtime Node path references are centralized"
fi
fi

if command -v varlock &>/dev/null || [ -x "$BAUDBOT_HOME/.varlock/bin/varlock" ]; then
pass "varlock is installed"
if [ -f "$BAUDBOT_HOME/.varlock/config.json" ] && grep -q '"anonymousId"' "$BAUDBOT_HOME/.varlock/config.json"; then
Expand Down
8 changes: 7 additions & 1 deletion bin/lib/baudbot-runtime.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#!/bin/bash
# Runtime/status/session helpers for bin/baudbot.

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
# shellcheck source=bin/lib/runtime-node.sh
source "$SCRIPT_DIR/runtime-node.sh"

# Detect systemd
has_systemd() {
command -v systemctl &>/dev/null && [ -d /run/systemd/system ]
Expand Down Expand Up @@ -434,7 +438,9 @@ cmd_attach() {
echo -e " ${GREEN}Agent keeps running under systemd in the background.${RESET}"
echo ""
pause_before_attach
exec sudo -u "$AGENT_USER" bash -lc "export PATH='$AGENT_HOME/.varlock/bin:$AGENT_HOME/opt/node-v22.14.0-linux-x64/bin':\$PATH; cd ~; varlock run --path ~/.config/ -- pi --session '$pi_target'"
local node_bin_dir=""
node_bin_dir="$(bb_resolve_runtime_node_bin_dir "$AGENT_HOME")"
exec sudo -u "$AGENT_USER" bash -lc "export PATH='$AGENT_HOME/.varlock/bin:$node_bin_dir':\$PATH; cd ~; varlock run --path ~/.config/ -- pi --session '$pi_target'"
}

choose_tmux_target() {
Expand Down
71 changes: 71 additions & 0 deletions bin/lib/runtime-node.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/bin/bash
# Shared embedded Node runtime helpers.

# Canonical embedded Node version used by setup/install unless overridden.
: "${BAUDBOT_RUNTIME_NODE_VERSION_DEFAULT:=22.14.0}"

bb_runtime_node_version() {
echo "${BAUDBOT_RUNTIME_NODE_VERSION:-$BAUDBOT_RUNTIME_NODE_VERSION_DEFAULT}"
}

bb_runtime_node_versioned_dir() {
local home_dir="${1:?home directory required}"
echo "$home_dir/opt/node-v$(bb_runtime_node_version)-linux-x64"
}

bb_runtime_node_bin_dir() {
local home_dir="${1:?home directory required}"

if [ -n "${BAUDBOT_RUNTIME_NODE_BIN_DIR:-}" ]; then
echo "$BAUDBOT_RUNTIME_NODE_BIN_DIR"
return 0
fi

if [ -n "${BAUDBOT_RUNTIME_NODE_DIR:-}" ]; then
echo "$BAUDBOT_RUNTIME_NODE_DIR/bin"
return 0
fi

echo "$home_dir/opt/node/bin"
}

bb_resolve_runtime_node_bin() {
local home_dir="${1:-${HOME:-}}"
local candidate=""

[ -n "$home_dir" ] || return 1

for candidate in \
"${BAUDBOT_RUNTIME_NODE_BIN:-}" \
"$(bb_runtime_node_bin_dir "$home_dir")/node" \
"$(bb_runtime_node_versioned_dir "$home_dir")/bin/node" \
"$home_dir/opt/node-v"*-linux-x64/bin/node; do
[ -n "$candidate" ] || continue

# If the glob didn't expand, skip the literal pattern.
case "$candidate" in
*\**)
continue
;;
esac

if [ -x "$candidate" ]; then
echo "$candidate"
return 0
fi
done

return 1
}

bb_resolve_runtime_node_bin_dir() {
local home_dir="${1:-${HOME:-}}"
local node_bin=""

if node_bin="$(bb_resolve_runtime_node_bin "$home_dir")"; then
dirname "$node_bin"
return 0
fi

bb_runtime_node_bin_dir "$home_dir"
}
22 changes: 22 additions & 0 deletions bin/runtime-node-paths.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
# Guardrail: embedded Node versioned paths must not be hardcoded.

set -euo pipefail

REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"

echo "=== runtime node path drift check ==="

if command -v rg >/dev/null 2>&1; then
matches="$(rg -n --glob '!node_modules/**' --glob '!.git/**' 'node-v[0-9]+\.[0-9]+\.[0-9]+-linux-x64' . || true)"
else
matches="$(grep -RInE --exclude-dir=node_modules --exclude-dir=.git 'node-v[0-9]+\.[0-9]+\.[0-9]+-linux-x64' . || true)"
fi
if [ -n "$matches" ]; then
echo "❌ Found hardcoded versioned Node paths:"
echo "$matches" | sed 's/^/ /'
exit 1
fi

echo "✅ No hardcoded versioned Node paths found"
4 changes: 3 additions & 1 deletion bin/security-audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/lib/shell-common.sh"
# shellcheck source=bin/lib/paths-common.sh
source "$SCRIPT_DIR/lib/paths-common.sh"
# shellcheck source=bin/lib/runtime-node.sh
source "$SCRIPT_DIR/lib/runtime-node.sh"
bb_enable_strict_mode
bb_init_paths

Expand Down Expand Up @@ -625,7 +627,7 @@ done

# Deep scan: cross-pattern analysis via Node scanner (deployed copies)
if [ "$DEEP" -eq 1 ]; then
NODE_BIN="$BAUDBOT_HOME/opt/node-v22.14.0-linux-x64/bin/node"
NODE_BIN="$(bb_resolve_runtime_node_bin "$BAUDBOT_HOME" || true)"
# Try source scanner first, fall back to deployed copy
SCANNER=""
[ -f "$BAUDBOT_SRC/bin/scan-extensions.mjs" ] && [ -r "$BAUDBOT_SRC/bin/scan-extensions.mjs" ] && SCANNER="$BAUDBOT_SRC/bin/scan-extensions.mjs"
Expand Down
6 changes: 4 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,11 @@ info "This takes 1–2 minutes."
echo ""

if [ "$EXPERIMENTAL" -eq 1 ]; then
BAUDBOT_PI_VERSION="${BAUDBOT_PI_VERSION:-}" bash "$REPO_DIR/setup.sh" --experimental "$ADMIN_USER"
BAUDBOT_PI_VERSION="${BAUDBOT_PI_VERSION:-}" BAUDBOT_RUNTIME_NODE_VERSION="${BAUDBOT_RUNTIME_NODE_VERSION:-}" \
bash "$REPO_DIR/setup.sh" --experimental "$ADMIN_USER"
else
BAUDBOT_PI_VERSION="${BAUDBOT_PI_VERSION:-}" bash "$REPO_DIR/setup.sh" "$ADMIN_USER"
BAUDBOT_PI_VERSION="${BAUDBOT_PI_VERSION:-}" BAUDBOT_RUNTIME_NODE_VERSION="${BAUDBOT_RUNTIME_NODE_VERSION:-}" \
bash "$REPO_DIR/setup.sh" "$ADMIN_USER"
fi

echo ""
Expand Down
4 changes: 2 additions & 2 deletions pi/skills/control-agent/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ git worktree add ~/workspace/worktrees/$BRANCH -b $BRANCH origin/main
# 2. Launch the agent IN the worktree
tmux new-session -d -s $SESSION_NAME \
"cd ~/workspace/worktrees/$BRANCH && \
export PATH=\$HOME/.varlock/bin:\$HOME/opt/node-v22.14.0-linux-x64/bin:\$PATH && \
export PATH=\$HOME/.varlock/bin:\$HOME/opt/node/bin:\$PATH && \
export PI_SESSION_NAME=$SESSION_NAME && \
exec varlock run --path ~/.config/ -- pi --session-control --skill ~/.pi/agent/skills/dev-agent --model <MODEL_FROM_TABLE_ABOVE>"
```
Expand Down Expand Up @@ -330,7 +330,7 @@ The sentry-agent triages Sentry alerts and investigates critical issues via the
| `OPENCODE_ZEN_API_KEY` | `opencode-zen/claude-haiku-4-5` |

```bash
tmux new-session -d -s sentry-agent "export PATH=\$HOME/.varlock/bin:\$HOME/opt/node-v22.14.0-linux-x64/bin:\$PATH && export PI_SESSION_NAME=sentry-agent && varlock run --path ~/.config/ -- pi --session-control --skill ~/.pi/agent/skills/sentry-agent --model <MODEL_FROM_TABLE_ABOVE>"
tmux new-session -d -s sentry-agent "export PATH=\$HOME/.varlock/bin:\$HOME/opt/node/bin:\$PATH && export PI_SESSION_NAME=sentry-agent && varlock run --path ~/.config/ -- pi --session-control --skill ~/.pi/agent/skills/sentry-agent --model <MODEL_FROM_TABLE_ABOVE>"
```

**Model note**: `github-copilot/*` models reject Personal Access Tokens and will fail in non-interactive sessions.
Expand Down
Loading