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
4 changes: 2 additions & 2 deletions .env.schema
Original file line number Diff line number Diff line change
Expand Up @@ -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=

Expand Down
4 changes: 2 additions & 2 deletions bin/baudbot
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 39 additions & 1 deletion bin/broker-register.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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",
"",
Expand All @@ -62,6 +64,7 @@ export function parseArgs(argv) {
registrationToken: "",
verbose: false,
help: false,
noRestart: false,
};

for (let i = 0; i < argv.length; i++) {
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
}
Expand Down
33 changes: 33 additions & 0 deletions bin/broker-register.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
isMainModule,
lookupUser,
resolveConfigTargets,
restartAgent,
hasSystemd,
} from "./broker-register.mjs";

const FIXTURE_SERVER_KEYS = {
Expand Down Expand Up @@ -48,6 +50,7 @@ test("parseArgs parses long-form options", () => {
registrationToken: "token-xyz",
verbose: false,
help: false,
noRestart: false,
});
});

Expand Down Expand Up @@ -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"]);
});
24 changes: 10 additions & 14 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down