From 9b0678f1c6682fcc020cf7394be82a0a33e10fe7 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Mon, 23 Feb 2026 20:15:40 -0500 Subject: [PATCH 1/4] refactor: shrink doctor/audit checks via shared node contracts --- bin/checks/slack-allowed-users.mjs | 76 +++++++++++++++++++++++++ bin/doctor.sh | 38 +++++-------- bin/lib/check-report-common.sh | 51 +++++++++++++++++ bin/lib/check-report-common.test.sh | 47 +++++++++++++++ bin/security-audit.sh | 55 ++++++++---------- package.json | 2 +- test/slack-allowed-users-check.test.mjs | 63 ++++++++++++++++++++ vitest.config.mjs | 1 + 8 files changed, 276 insertions(+), 57 deletions(-) create mode 100755 bin/checks/slack-allowed-users.mjs create mode 100644 test/slack-allowed-users-check.test.mjs diff --git a/bin/checks/slack-allowed-users.mjs b/bin/checks/slack-allowed-users.mjs new file mode 100755 index 0000000..022ee70 --- /dev/null +++ b/bin/checks/slack-allowed-users.mjs @@ -0,0 +1,76 @@ +#!/usr/bin/env node + +import fs from "node:fs"; + +const envPath = process.argv[2] || ""; + +function countUsers(rawValue) { + if (!rawValue) return 0; + return rawValue + .split(",") + .filter((entry) => entry.length > 0).length; +} + +if (!envPath) { + process.stdout.write( + JSON.stringify({ + ok: "0", + exists: "0", + defined: "0", + raw_non_empty: "0", + count: "0", + error: "missing_path_argument", + }), + ); + process.exit(0); +} + +if (!fs.existsSync(envPath)) { + process.stdout.write( + JSON.stringify({ + ok: "1", + exists: "0", + defined: "0", + raw_non_empty: "0", + count: "0", + error: "", + }), + ); + process.exit(0); +} + +try { + const lines = fs.readFileSync(envPath, "utf8").split(/\r?\n/); + let value = ""; + let defined = false; + + for (const line of lines) { + if (!line.startsWith("SLACK_ALLOWED_USERS=")) continue; + value = line.slice("SLACK_ALLOWED_USERS=".length); + defined = true; + } + + const count = defined ? countUsers(value) : 0; + + process.stdout.write( + JSON.stringify({ + ok: "1", + exists: "1", + defined: defined ? "1" : "0", + raw_non_empty: defined && value.length > 0 ? "1" : "0", + count: String(count), + error: "", + }), + ); +} catch { + process.stdout.write( + JSON.stringify({ + ok: "0", + exists: "1", + defined: "0", + raw_non_empty: "0", + count: "0", + error: "parse_error", + }), + ); +} diff --git a/bin/doctor.sh b/bin/doctor.sh index e48b3df..440e2fa 100755 --- a/bin/doctor.sh +++ b/bin/doctor.sh @@ -11,6 +11,8 @@ 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/json-common.sh +source "$SCRIPT_DIR/lib/json-common.sh" # shellcheck source=bin/lib/runtime-node.sh source "$SCRIPT_DIR/lib/runtime-node.sh" bb_enable_strict_mode @@ -64,6 +66,8 @@ else fail "Node.js not found (expected: $NODE_BIN)" fi +CHECK_NODE_BIN="$(bb_pick_node_bin "${NODE_BIN:-}" || true)" + PI_BIN="$(bb_resolve_runtime_node_bin_dir "$BAUDBOT_HOME")/pi" if [ -x "$PI_BIN" ] || [ -L "$PI_BIN" ]; then pass "pi is installed" @@ -240,7 +244,10 @@ if [ -f "$ENV_FILE" ]; then fi fi - if grep -q '^SLACK_ALLOWED_USERS=.\+' "$ENV_FILE" 2>/dev/null; then + SLACK_ALLOWED_USERS_CHECK_SCRIPT="$BAUDBOT_ROOT/bin/checks/slack-allowed-users.mjs" + slack_allowed_users_payload="$(bb_run_node_check_payload "$CHECK_NODE_BIN" "$SLACK_ALLOWED_USERS_CHECK_SCRIPT" "$ENV_FILE")" + slack_allowed_users_non_empty="$(bb_json_field_or_default "$slack_allowed_users_payload" "raw_non_empty" "0")" + if [ "$slack_allowed_users_non_empty" = "1" ]; then pass "SLACK_ALLOWED_USERS is set" else warn "SLACK_ALLOWED_USERS is not set (all workspace members allowed)" @@ -302,31 +309,14 @@ fi INTEGRITY_STATUS_FILE="$BAUDBOT_INTEGRITY_STATUS_FILE" INTEGRITY_CHECK_SCRIPT="$BAUDBOT_ROOT/bin/checks/integrity-status.mjs" -INTEGRITY_CHECK_NODE_BIN="" -if [ -n "${NODE_BIN:-}" ] && [ -x "${NODE_BIN:-}" ]; then - INTEGRITY_CHECK_NODE_BIN="$NODE_BIN" -elif command -v node >/dev/null 2>&1; then - INTEGRITY_CHECK_NODE_BIN="$(command -v node)" -fi - -if [ -n "$INTEGRITY_CHECK_NODE_BIN" ] && [ -f "$INTEGRITY_CHECK_SCRIPT" ]; then - integrity_payload="$($INTEGRITY_CHECK_NODE_BIN "$INTEGRITY_CHECK_SCRIPT" "$INTEGRITY_STATUS_FILE" 2>/dev/null || true)" -else - integrity_payload="" -fi +integrity_payload="$(bb_run_node_check_payload "$CHECK_NODE_BIN" "$INTEGRITY_CHECK_SCRIPT" "$INTEGRITY_STATUS_FILE")" if [ -n "$integrity_payload" ]; then - integrity_exists="$(printf '%s' "$integrity_payload" | json_get_string_stdin "exists" 2>/dev/null || true)" - integrity_status="$(printf '%s' "$integrity_payload" | json_get_string_stdin "status" 2>/dev/null || true)" - integrity_checked_at="$(printf '%s' "$integrity_payload" | json_get_string_stdin "checked_at" 2>/dev/null || true)" - integrity_missing="$(printf '%s' "$integrity_payload" | json_get_string_stdin "missing_files" 2>/dev/null || true)" - integrity_mismatches="$(printf '%s' "$integrity_payload" | json_get_string_stdin "hash_mismatches" 2>/dev/null || true)" - - [ -n "$integrity_exists" ] || integrity_exists="0" - [ -n "$integrity_status" ] || integrity_status="unknown" - [ -n "$integrity_checked_at" ] || integrity_checked_at="unknown" - [ -n "$integrity_missing" ] || integrity_missing="0" - [ -n "$integrity_mismatches" ] || integrity_mismatches="0" + integrity_exists="$(bb_json_field_or_default "$integrity_payload" "exists" "0")" + integrity_status="$(bb_json_field_or_default "$integrity_payload" "status" "unknown")" + integrity_checked_at="$(bb_json_field_or_default "$integrity_payload" "checked_at" "unknown")" + integrity_missing="$(bb_json_field_or_default "$integrity_payload" "missing_files" "0")" + integrity_mismatches="$(bb_json_field_or_default "$integrity_payload" "hash_mismatches" "0")" if [ "$integrity_exists" != "1" ]; then if [ "$IS_ROOT" -ne 1 ] && [ -d "$BAUDBOT_HOME/.pi/agent" ]; then diff --git a/bin/lib/check-report-common.sh b/bin/lib/check-report-common.sh index a761e6b..2864e54 100644 --- a/bin/lib/check-report-common.sh +++ b/bin/lib/check-report-common.sh @@ -27,3 +27,54 @@ bb_summary_print_item() { printf " %s %-9s %s\n" "$icon" "$label:" "$value" } + +bb_json_field_or_default() { + local payload="$1" + local key="$2" + local fallback="$3" + local value="" + + if [ -z "$payload" ]; then + echo "$fallback" + return 0 + fi + + value="$(printf '%s' "$payload" | json_get_string_stdin "$key" 2>/dev/null || true)" + if [ -n "$value" ]; then + echo "$value" + else + echo "$fallback" + fi +} + +bb_pick_node_bin() { + local preferred_bin="${1:-}" + + if [ -n "$preferred_bin" ] && [ -x "$preferred_bin" ]; then + echo "$preferred_bin" + return 0 + fi + + if command -v node >/dev/null 2>&1; then + command -v node + return 0 + fi + + return 1 +} + +bb_run_node_check_payload() { + local node_bin="${1:-}" + local script_path="${2:-}" + shift 2 || true + + if [ -z "$node_bin" ] || [ ! -x "$node_bin" ]; then + return 0 + fi + + if [ -z "$script_path" ] || [ ! -f "$script_path" ]; then + return 0 + fi + + "$node_bin" "$script_path" "$@" 2>/dev/null || true +} diff --git a/bin/lib/check-report-common.test.sh b/bin/lib/check-report-common.test.sh index 60a9c25..04822f5 100644 --- a/bin/lib/check-report-common.test.sh +++ b/bin/lib/check-report-common.test.sh @@ -4,6 +4,8 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=bin/lib/json-common.sh +source "$SCRIPT_DIR/json-common.sh" # shellcheck source=bin/lib/check-report-common.sh source "$SCRIPT_DIR/check-report-common.sh" @@ -71,12 +73,57 @@ test_summary_helpers_render_rows() { ) } +test_json_field_or_default_reads_existing_key() { + ( + set -euo pipefail + payload='{"status":"pass"}' + [ "$(bb_json_field_or_default "$payload" status unknown)" = "pass" ] + ) +} + +test_json_field_or_default_falls_back_when_missing() { + ( + set -euo pipefail + payload='{"exists":"1"}' + [ "$(bb_json_field_or_default "$payload" status unknown)" = "unknown" ] + ) +} + +test_pick_node_bin_uses_preferred() { + ( + set -euo pipefail + fake_node="$(command -v node)" + [ -n "$(bb_pick_node_bin "$fake_node")" ] + ) +} + +test_run_node_check_payload_executes_script() { + ( + set -euo pipefail + tmp_script="$(mktemp /tmp/check-report-node-check.XXXXXX.mjs)" + trap 'rm -f "$tmp_script"' EXIT + + cat >"$tmp_script" <<'EOF' +#!/usr/bin/env node +process.stdout.write(JSON.stringify({ ok: "1" })); +EOF + chmod +x "$tmp_script" + + output="$(bb_run_node_check_payload "$(command -v node)" "$tmp_script")" + [ "$(bb_json_field_or_default "$output" ok 0)" = "1" ] + ) +} + echo "=== check-report-common tests ===" echo "" run_test "reset many sets counters to zero" test_reset_many_sets_zero run_test "increment updates named counter" test_inc_increments_named_counter run_test "summary helpers render rows" test_summary_helpers_render_rows +run_test "json field helper reads key" test_json_field_or_default_reads_existing_key +run_test "json field helper falls back when missing" test_json_field_or_default_falls_back_when_missing +run_test "node picker prefers runtime path" test_pick_node_bin_uses_preferred +run_test "node check runner executes script" test_run_node_check_payload_executes_script echo "" echo "=== $PASSED/$TOTAL passed, $FAILED failed ===" diff --git a/bin/security-audit.sh b/bin/security-audit.sh index 970dbd2..99e35df 100755 --- a/bin/security-audit.sh +++ b/bin/security-audit.sh @@ -289,30 +289,16 @@ else "Run deploy.sh to generate" fi -INTEGRITY_CHECK_SCRIPT="$BAUDBOT_SRC/bin/checks/integrity-status.mjs" -INTEGRITY_CHECK_NODE_BIN="$(bb_resolve_runtime_node_bin "$BAUDBOT_HOME" 2>/dev/null || true)" -if [ -z "$INTEGRITY_CHECK_NODE_BIN" ] && command -v node >/dev/null 2>&1; then - INTEGRITY_CHECK_NODE_BIN="$(command -v node)" -fi - -if [ -n "$INTEGRITY_CHECK_NODE_BIN" ] && [ -f "$INTEGRITY_CHECK_SCRIPT" ]; then - integrity_payload="$($INTEGRITY_CHECK_NODE_BIN "$INTEGRITY_CHECK_SCRIPT" "$BAUDBOT_INTEGRITY_STATUS_FILE" 2>/dev/null || true)" -else - integrity_payload="" -fi +CHECK_NODE_BIN="$(bb_pick_node_bin "$(bb_resolve_runtime_node_bin "$BAUDBOT_HOME" 2>/dev/null || true)" || true)" +INTEGRITY_CHECK_SCRIPT="$SCRIPT_DIR/checks/integrity-status.mjs" +integrity_payload="$(bb_run_node_check_payload "$CHECK_NODE_BIN" "$INTEGRITY_CHECK_SCRIPT" "$BAUDBOT_INTEGRITY_STATUS_FILE")" if [ -n "$integrity_payload" ]; then - status_exists="$(printf '%s' "$integrity_payload" | json_get_string_stdin "exists" 2>/dev/null || true)" - status_value="$(printf '%s' "$integrity_payload" | json_get_string_stdin "status" 2>/dev/null || true)" - status_checked_at="$(printf '%s' "$integrity_payload" | json_get_string_stdin "checked_at" 2>/dev/null || true)" - status_missing="$(printf '%s' "$integrity_payload" | json_get_string_stdin "missing_files" 2>/dev/null || true)" - status_mismatches="$(printf '%s' "$integrity_payload" | json_get_string_stdin "hash_mismatches" 2>/dev/null || true)" - - [ -n "$status_exists" ] || status_exists="0" - [ -n "$status_value" ] || status_value="unknown" - [ -n "$status_checked_at" ] || status_checked_at="unknown" - [ -n "$status_missing" ] || status_missing="0" - [ -n "$status_mismatches" ] || status_mismatches="0" + status_exists="$(bb_json_field_or_default "$integrity_payload" "exists" "0")" + status_value="$(bb_json_field_or_default "$integrity_payload" "status" "unknown")" + status_checked_at="$(bb_json_field_or_default "$integrity_payload" "checked_at" "unknown")" + status_missing="$(bb_json_field_or_default "$integrity_payload" "missing_files" "0")" + status_mismatches="$(bb_json_field_or_default "$integrity_payload" "hash_mismatches" "0")" if [ "$status_exists" != "1" ]; then finding "WARN" "No startup integrity status found" \ @@ -731,18 +717,23 @@ echo "" echo "Bridge Configuration" # Check SLACK_ALLOWED_USERS mode (without reading the actual value) -if [ -f "$BAUDBOT_HOME/.config/.env" ]; then - if grep -q '^SLACK_ALLOWED_USERS=' "$BAUDBOT_HOME/.config/.env" 2>/dev/null; then - allowed_count=$(grep '^SLACK_ALLOWED_USERS=' "$BAUDBOT_HOME/.config/.env" 2>/dev/null | cut -d= -f2 | tr ',' '\n' | grep -c . || echo 0) - if [ "$allowed_count" -gt 0 ]; then - ok "SLACK_ALLOWED_USERS configured ($allowed_count user(s))" - else - finding "WARN" "SLACK_ALLOWED_USERS is empty" \ - "Bridge will allow all workspace members" - fi - else +SLACK_ALLOWED_USERS_CHECK_SCRIPT="$SCRIPT_DIR/checks/slack-allowed-users.mjs" +SLACK_ALLOWED_USERS_ENV_FILE="$BAUDBOT_HOME/.config/.env" +slack_allowed_users_payload="$(bb_run_node_check_payload "$CHECK_NODE_BIN" "$SLACK_ALLOWED_USERS_CHECK_SCRIPT" "$SLACK_ALLOWED_USERS_ENV_FILE")" + +slack_allowed_users_exists="$(bb_json_field_or_default "$slack_allowed_users_payload" "exists" "0")" +slack_allowed_users_defined="$(bb_json_field_or_default "$slack_allowed_users_payload" "defined" "0")" +slack_allowed_users_count="$(bb_json_field_or_default "$slack_allowed_users_payload" "count" "0")" + +if [ "$slack_allowed_users_exists" = "1" ]; then + if [ "$slack_allowed_users_defined" != "1" ]; then finding "WARN" "SLACK_ALLOWED_USERS not set in .env" \ "Bridge will allow all workspace members" + elif [ "$slack_allowed_users_count" -gt 0 ]; then + ok "SLACK_ALLOWED_USERS configured ($slack_allowed_users_count user(s))" + else + finding "WARN" "SLACK_ALLOWED_USERS is empty" \ + "Bridge will allow all workspace members" fi fi echo "" diff --git a/package.json b/package.json index 8bcd955..fc0bec3 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "test": "vitest run --config vitest.config.mjs", - "test:js": "vitest run --config vitest.config.mjs pi/extensions/heartbeat.test.mjs pi/extensions/memory.test.mjs test/legacy-node-tests.test.mjs test/broker-bridge.integration.test.mjs test/integrity-status-check.test.mjs", + "test:js": "vitest run --config vitest.config.mjs pi/extensions/heartbeat.test.mjs pi/extensions/memory.test.mjs test/legacy-node-tests.test.mjs test/broker-bridge.integration.test.mjs test/integrity-status-check.test.mjs test/slack-allowed-users-check.test.mjs", "test:shell": "vitest run --config vitest.config.mjs test/shell-scripts.test.mjs test/security-audit.test.mjs", "test:coverage": "vitest run --config vitest.config.mjs --coverage pi/extensions/heartbeat.test.mjs pi/extensions/memory.test.mjs test/legacy-node-tests.test.mjs", "lint": "npm run lint:js && npm run lint:shell", diff --git a/test/slack-allowed-users-check.test.mjs b/test/slack-allowed-users-check.test.mjs new file mode 100644 index 0000000..8877709 --- /dev/null +++ b/test/slack-allowed-users-check.test.mjs @@ -0,0 +1,63 @@ +import { spawnSync } from "node:child_process"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { afterEach, describe, expect, it } from "vitest"; + +const scriptPath = path.resolve("bin/checks/slack-allowed-users.mjs"); +const tmpDirs = []; + +function runCheck(envPath) { + const result = spawnSync("node", [scriptPath, envPath], { + cwd: path.resolve("."), + encoding: "utf8", + }); + + expect(result.status).toBe(0); + return JSON.parse(result.stdout); +} + +afterEach(() => { + for (const dir of tmpDirs.splice(0)) { + fs.rmSync(dir, { recursive: true, force: true }); + } +}); + +describe("bin/checks/slack-allowed-users.mjs", () => { + it("reports when env file is missing", () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "baudbot-slack-users-check-")); + tmpDirs.push(tmpDir); + + const payload = runCheck(path.join(tmpDir, ".env")); + expect(payload.exists).toBe("0"); + expect(payload.defined).toBe("0"); + expect(payload.count).toBe("0"); + }); + + it("reports configured users count", () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "baudbot-slack-users-check-")); + tmpDirs.push(tmpDir); + + const envPath = path.join(tmpDir, ".env"); + fs.writeFileSync(envPath, "SLACK_ALLOWED_USERS=U1,U2,U3\n"); + + const payload = runCheck(envPath); + expect(payload.exists).toBe("1"); + expect(payload.defined).toBe("1"); + expect(payload.raw_non_empty).toBe("1"); + expect(payload.count).toBe("3"); + }); + + it("handles empty allowed users setting", () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "baudbot-slack-users-check-")); + tmpDirs.push(tmpDir); + + const envPath = path.join(tmpDir, ".env"); + fs.writeFileSync(envPath, "SLACK_ALLOWED_USERS=\n"); + + const payload = runCheck(envPath); + expect(payload.defined).toBe("1"); + expect(payload.raw_non_empty).toBe("0"); + expect(payload.count).toBe("0"); + }); +}); diff --git a/vitest.config.mjs b/vitest.config.mjs index f30921a..e039bbf 100644 --- a/vitest.config.mjs +++ b/vitest.config.mjs @@ -8,6 +8,7 @@ export default defineConfig({ "test/legacy-node-tests.test.mjs", "test/broker-bridge.integration.test.mjs", "test/integrity-status-check.test.mjs", + "test/slack-allowed-users-check.test.mjs", "test/shell-scripts.test.mjs", "test/security-audit.test.mjs", ], From b2e21fc1e5cddad22256adb68aa669d01adecf3a Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Mon, 23 Feb 2026 21:08:27 -0500 Subject: [PATCH 2/4] fix: trim slack allowed users entries in audit check --- bin/checks/slack-allowed-users.mjs | 1 + test/slack-allowed-users-check.test.mjs | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/bin/checks/slack-allowed-users.mjs b/bin/checks/slack-allowed-users.mjs index 022ee70..f8a4993 100755 --- a/bin/checks/slack-allowed-users.mjs +++ b/bin/checks/slack-allowed-users.mjs @@ -8,6 +8,7 @@ function countUsers(rawValue) { if (!rawValue) return 0; return rawValue .split(",") + .map((entry) => entry.trim()) .filter((entry) => entry.length > 0).length; } diff --git a/test/slack-allowed-users-check.test.mjs b/test/slack-allowed-users-check.test.mjs index 8877709..ebef5f5 100644 --- a/test/slack-allowed-users-check.test.mjs +++ b/test/slack-allowed-users-check.test.mjs @@ -48,6 +48,19 @@ describe("bin/checks/slack-allowed-users.mjs", () => { expect(payload.count).toBe("3"); }); + it("ignores whitespace-only entries", () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "baudbot-slack-users-check-")); + tmpDirs.push(tmpDir); + + const envPath = path.join(tmpDir, ".env"); + fs.writeFileSync(envPath, "SLACK_ALLOWED_USERS=U1, , U2\n"); + + const payload = runCheck(envPath); + expect(payload.defined).toBe("1"); + expect(payload.raw_non_empty).toBe("1"); + expect(payload.count).toBe("2"); + }); + it("handles empty allowed users setting", () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "baudbot-slack-users-check-")); tmpDirs.push(tmpDir); From b118fa710ccaec8fe935e89c7a982f315b893d9a Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Mon, 23 Feb 2026 21:17:33 -0500 Subject: [PATCH 3/4] test: switch to glob-based vitest discovery --- package.json | 6 +++--- ...-audit.test.mjs => security-audit.shell.test.mjs} | 0 ...scripts.test.mjs => shell-scripts.shell.test.mjs} | 0 vitest.config.mjs | 12 ++---------- vitest.js.config.mjs | 11 +++++++++++ vitest.shell.config.mjs | 10 ++++++++++ 6 files changed, 26 insertions(+), 13 deletions(-) rename test/{security-audit.test.mjs => security-audit.shell.test.mjs} (100%) rename test/{shell-scripts.test.mjs => shell-scripts.shell.test.mjs} (100%) create mode 100644 vitest.js.config.mjs create mode 100644 vitest.shell.config.mjs diff --git a/package.json b/package.json index fc0bec3..3841483 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "private": true, "scripts": { "test": "vitest run --config vitest.config.mjs", - "test:js": "vitest run --config vitest.config.mjs pi/extensions/heartbeat.test.mjs pi/extensions/memory.test.mjs test/legacy-node-tests.test.mjs test/broker-bridge.integration.test.mjs test/integrity-status-check.test.mjs test/slack-allowed-users-check.test.mjs", - "test:shell": "vitest run --config vitest.config.mjs test/shell-scripts.test.mjs test/security-audit.test.mjs", - "test:coverage": "vitest run --config vitest.config.mjs --coverage pi/extensions/heartbeat.test.mjs pi/extensions/memory.test.mjs test/legacy-node-tests.test.mjs", + "test:js": "vitest run --config vitest.js.config.mjs", + "test:shell": "vitest run --config vitest.shell.config.mjs", + "test:coverage": "vitest run --config vitest.js.config.mjs --coverage", "lint": "npm run lint:js && npm run lint:shell", "lint:js": "biome lint", "lint:shell": "bash bin/lint-shell.sh", diff --git a/test/security-audit.test.mjs b/test/security-audit.shell.test.mjs similarity index 100% rename from test/security-audit.test.mjs rename to test/security-audit.shell.test.mjs diff --git a/test/shell-scripts.test.mjs b/test/shell-scripts.shell.test.mjs similarity index 100% rename from test/shell-scripts.test.mjs rename to test/shell-scripts.shell.test.mjs diff --git a/vitest.config.mjs b/vitest.config.mjs index e039bbf..3e0668e 100644 --- a/vitest.config.mjs +++ b/vitest.config.mjs @@ -2,16 +2,8 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { - include: [ - "pi/extensions/heartbeat.test.mjs", - "pi/extensions/memory.test.mjs", - "test/legacy-node-tests.test.mjs", - "test/broker-bridge.integration.test.mjs", - "test/integrity-status-check.test.mjs", - "test/slack-allowed-users-check.test.mjs", - "test/shell-scripts.test.mjs", - "test/security-audit.test.mjs", - ], + include: ["pi/extensions/**/*.test.mjs", "test/**/*.test.mjs"], + exclude: ["pi/extensions/tool-guard.test.mjs"], testTimeout: 120_000, hookTimeout: 120_000, fileParallelism: false, diff --git a/vitest.js.config.mjs b/vitest.js.config.mjs new file mode 100644 index 0000000..4d11dbf --- /dev/null +++ b/vitest.js.config.mjs @@ -0,0 +1,11 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["pi/extensions/**/*.test.mjs", "test/**/*.test.mjs"], + exclude: ["test/**/*.shell.test.mjs", "pi/extensions/tool-guard.test.mjs"], + testTimeout: 120_000, + hookTimeout: 120_000, + fileParallelism: false, + }, +}); diff --git a/vitest.shell.config.mjs b/vitest.shell.config.mjs new file mode 100644 index 0000000..a7c7c2a --- /dev/null +++ b/vitest.shell.config.mjs @@ -0,0 +1,10 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["test/**/*.shell.test.mjs"], + testTimeout: 120_000, + hookTimeout: 120_000, + fileParallelism: false, + }, +}); From 8634a574f631dc6c1fb7e04ec6cf088ee5825af5 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Mon, 23 Feb 2026 23:00:27 -0500 Subject: [PATCH 4/4] bridge: migrate protected slack-bridge path to /opt release --- pi/skills/control-agent/startup-cleanup.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pi/skills/control-agent/startup-cleanup.sh b/pi/skills/control-agent/startup-cleanup.sh index e6383e2..4f62c83 100755 --- a/pi/skills/control-agent/startup-cleanup.sh +++ b/pi/skills/control-agent/startup-cleanup.sh @@ -22,6 +22,14 @@ if [ -r "$RUNTIME_NODE_HELPER" ]; then source "$RUNTIME_NODE_HELPER" fi +NODE_BIN_DIR="${NODE_BIN_DIR:-$HOME/opt/node/bin}" +if command -v bb_resolve_runtime_node_bin_dir >/dev/null 2>&1; then + NODE_BIN_DIR="$(bb_resolve_runtime_node_bin_dir "$HOME")" +fi + +BAUDBOT_CURRENT_LINK="${BAUDBOT_CURRENT_LINK:-/opt/baudbot/current}" +BAUDBOT_ENV_FILE="${BAUDBOT_ENV_FILE:-$HOME/.config/.env}" + SOCKET_DIR="$HOME/.pi/session-control" if [ $# -eq 0 ]; then @@ -79,7 +87,7 @@ fi BRIDGE_LOG_DIR="$HOME/.pi/agent/logs" BRIDGE_LOG_FILE="$BRIDGE_LOG_DIR/slack-bridge.log" -BRIDGE_DIR="/opt/baudbot/current/slack-bridge" +BRIDGE_DIR="$BAUDBOT_CURRENT_LINK/slack-bridge" BRIDGE_TMUX_SESSION="slack-bridge" mkdir -p "$BRIDGE_LOG_DIR" @@ -147,10 +155,6 @@ fi # The tmux session stays alive independently of this script (same pattern as # sentry-agent). If the bridge crashes, the loop restarts it after 5 seconds. echo "Starting slack-bridge ($BRIDGE_SCRIPT) via tmux..." -NODE_BIN_DIR="${NODE_BIN_DIR:-$HOME/opt/node/bin}" -if command -v bb_resolve_runtime_node_bin_dir >/dev/null 2>&1; then - NODE_BIN_DIR="$(bb_resolve_runtime_node_bin_dir "$HOME")" -fi if [ ! -d "$NODE_BIN_DIR" ]; then # Fallback: resolve versioned node dir NODE_BIN_DIR="$(echo "$HOME"/opt/node-v*-linux-x64/bin | awk '{print $1}')" @@ -164,7 +168,7 @@ tmux new-session -d -s "$BRIDGE_TMUX_SESSION" "\ while true; do \ echo \"[\$(date -Is)] bridge: starting $BRIDGE_SCRIPT\" >> $BRIDGE_LOG_FILE; \ for v in \$(env | grep ^SLACK_BROKER_ | cut -d= -f1 || true); do unset \$v; done; \ - set -a; source \$HOME/.config/.env; set +a; \ + set -a; source $BAUDBOT_ENV_FILE; set +a; \ node $BRIDGE_SCRIPT >> $BRIDGE_LOG_FILE 2>&1; \ exit_code=\$?; \ echo \"[\$(date -Is)] bridge: exited with code \$exit_code, restarting in 5s\" >> $BRIDGE_LOG_FILE; \