diff --git a/.env.schema b/.env.schema index 9a0680b..adb0ab9 100644 --- a/.env.schema +++ b/.env.schema @@ -47,8 +47,8 @@ SLACK_BOT_TOKEN= SLACK_APP_TOKEN= # Comma-separated Slack user IDs allowed to interact with the agent -# Bridge refuses to start without at least one user ID. -# @required @sensitive=false @type=string +# Optional — if unset, all workspace members can interact. +# @sensitive=false @type=string # @example="U01ABCDEF,U02GHIJKL" SLACK_ALLOWED_USERS= diff --git a/bin/baudbot b/bin/baudbot index c206790..79c11ef 100755 --- a/bin/baudbot +++ b/bin/baudbot @@ -474,11 +474,11 @@ case "$COMMAND_NAME" in exec "$NODE_BIN" "$BAUDBOT_ROOT/bin/broker-register.mjs" "$@" ;; --help|-h|"") - echo "Usage: sudo baudbot broker register [--broker-url URL] [--workspace-id ID] --registration-token TOKEN [-v|--verbose]" + echo "Usage: sudo baudbot broker register [--broker-url URL] [--workspace-id ID] --registration-token TOKEN [--no-restart] [-v|--verbose]" ;; *) echo "Unknown broker subcommand: ${1:-}" - echo "Usage: sudo baudbot broker register [--broker-url URL] [--workspace-id ID] --registration-token TOKEN [-v|--verbose]" + echo "Usage: sudo baudbot broker register [--broker-url URL] [--workspace-id ID] --registration-token TOKEN [--no-restart] [-v|--verbose]" exit 1 ;; esac diff --git a/bin/broker-register.mjs b/bin/broker-register.mjs index 875bb57..9e1db42 100755 --- a/bin/broker-register.mjs +++ b/bin/broker-register.mjs @@ -12,6 +12,7 @@ * - agent config: /home/baudbot_agent/.config/.env */ +import { execFileSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -48,6 +49,7 @@ export function usageText() { " --broker-url URL Broker base URL (e.g. https://broker.example.com)", " --workspace-id ID Slack workspace ID (e.g. T0123ABCD)", " --registration-token TOKEN Registration token from dashboard callback (required)", + " --no-restart Skip automatic agent restart after registration", " -v, --verbose Show detailed registration progress", " -h, --help Show this help", "", @@ -62,6 +64,7 @@ export function parseArgs(argv) { registrationToken: "", verbose: false, help: false, + noRestart: false, }; for (let i = 0; i < argv.length; i++) { @@ -77,6 +80,11 @@ export function parseArgs(argv) { continue; } + if (arg === "--no-restart") { + out.noRestart = true; + continue; + } + if (arg.startsWith("--broker-url=")) { out.brokerUrl = arg.slice("--broker-url=".length); continue; @@ -558,6 +566,32 @@ export function isMainModule(moduleUrl = import.meta.url, argv1 = process.argv[1 return moduleUrl === argvUrl || (argvRealUrl !== "" && moduleUrl === argvRealUrl); } +export function hasSystemd() { + try { + fs.accessSync("/run/systemd/system", fs.constants.F_OK); + return true; + } catch { + return false; + } +} + +export function restartAgent({ logger = () => {}, execFileSyncImpl = execFileSync } = {}) { + if (!hasSystemd()) { + console.log("⚠️ systemd not available — restart the agent manually."); + return false; + } + + logger("Restarting agent via systemctl..."); + try { + execFileSyncImpl("systemctl", ["restart", "baudbot"], { stdio: "inherit" }); + console.log("✅ Agent restarted."); + return true; + } catch { + console.error("⚠️ Agent restart failed — run: sudo baudbot restart"); + return false; + } +} + export async function main(argv = process.argv.slice(2)) { const parsed = parseArgs(argv); @@ -597,7 +631,11 @@ export async function main(argv = process.argv.slice(2)) { console.log(` - ${target.path}`); } - console.log("Next step: sudo baudbot restart"); + if (parsed.noRestart) { + console.log("Next step: sudo baudbot restart"); + } else { + restartAgent({ logger }); + } return 0; } diff --git a/bin/broker-register.test.mjs b/bin/broker-register.test.mjs index a1417b3..e137b83 100644 --- a/bin/broker-register.test.mjs +++ b/bin/broker-register.test.mjs @@ -16,6 +16,8 @@ import { isMainModule, lookupUser, resolveConfigTargets, + restartAgent, + hasSystemd, } from "./broker-register.mjs"; const FIXTURE_SERVER_KEYS = { @@ -48,6 +50,7 @@ test("parseArgs parses long-form options", () => { registrationToken: "token-xyz", verbose: false, help: false, + noRestart: false, }); }); @@ -341,3 +344,33 @@ test("resolveConfigTargets throws for unknown BAUDBOT_CONFIG_USER", () => { /admin user not found/, ); }); + +test("parseArgs accepts --no-restart flag", () => { + const parsed = parseArgs(["--no-restart", "--registration-token", "tok"]); + assert.equal(parsed.noRestart, true); + assert.equal(parsed.registrationToken, "tok"); +}); + +test("parseArgs defaults noRestart to false", () => { + const parsed = parseArgs([]); + assert.equal(parsed.noRestart, false); +}); + +test("restartAgent returns false and warns when execFileSync fails", { skip: !hasSystemd() && "no systemd" }, () => { + const logs = []; + const logger = (msg) => logs.push(msg); + const failingExec = () => { throw new Error("simulated failure"); }; + + const result = restartAgent({ logger, execFileSyncImpl: failingExec }); + assert.equal(result, false); +}); + +test("restartAgent calls systemctl restart baudbot on success", { skip: !hasSystemd() && "no systemd" }, () => { + const calls = []; + const mockExec = (cmd, args) => { calls.push({ cmd, args }); }; + + restartAgent({ execFileSyncImpl: mockExec }); + assert.equal(calls.length, 1); + assert.equal(calls[0].cmd, "systemctl"); + assert.deepEqual(calls[0].args, ["restart", "baudbot"]); +}); diff --git a/install.sh b/install.sh index a37baa1..3cc7a11 100755 --- a/install.sh +++ b/install.sh @@ -107,28 +107,24 @@ info "Detected: ${BOLD}$PRETTY_NAME${RESET} ($DISTRO)" # ── Detect admin user ──────────────────────────────────────────────────────── -# If run via sudo, SUDO_USER is the real user. Otherwise ask. +# If run via sudo, SUDO_USER is the real user. Otherwise detect or ask. ADMIN_USER="${SUDO_USER:-}" if [ -z "$ADMIN_USER" ] || [ "$ADMIN_USER" = "root" ]; then - # Try to find a non-root user with a home directory - ADMIN_USER="" + # Try to find a non-root user with a home directory as the default candidate + CANDIDATE="" while IFS=: read -r username _ uid _ _ home _; do if [ "$uid" -ge 1000 ] && [ "$uid" -lt 60000 ] && [ -d "$home" ] && [ "$username" != "baudbot_agent" ]; then - ADMIN_USER="$username" + CANDIDATE="$username" break fi done < /etc/passwd - if [ -z "$ADMIN_USER" ]; then - ask "Admin username (your account, not root): " - read -r ADMIN_USER - else - ask "Admin username [${ADMIN_USER}]: " - read -r input - if [ -n "$input" ]; then - ADMIN_USER="$input" - fi - fi + # Fall back to current user (typically root when running directly as root) + CANDIDATE="${CANDIDATE:-$(whoami)}" + + ask "Admin username [${CANDIDATE}]: " + read -r input + ADMIN_USER="${input:-$CANDIDATE}" fi if ! id "$ADMIN_USER" &>/dev/null; then