From f1728f31645ef4ae9762f65152a1eb0c2680273e Mon Sep 17 00:00:00 2001 From: Michael Heller <21163552+mdheller@users.noreply.github.com> Date: Mon, 20 Jul 2026 07:32:53 -0400 Subject: [PATCH] packaging(cockpit): install the sovereign cockpit as brew CLIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OBJ-A last mile — the assembled cockpit now installs + runs as one product, not just from a staging dir. - Homebrew formula: two CLIs alongside the existing sidecar wrappers — bearbrowser-cockpit-assemble → assemble-cockpit.sh (build client-vue + sidecar) bearbrowser-cockpit-up → the governed orchestrator Both target a WRITABLE user data dir (${XDG_DATA_HOME:-~/.local/share}/bearbrowser/ cockpit-app/…) because the Cellar is read-only — assemble can't build into libexec. Kept LEAN: no node/bun added as hard deps on the whole browser; assemble checks + guides (caveat notes 'brew install node bun'). Caveats + a fast test added. - bearbrowser-cockpit-up: honor BEARBROWSER_COCKPIT_RES so the installed CLI finds the user-dir app (dev/staging still auto-derives from the script's parent). Verified: ruby -c OK; cockpit-up errors cleanly + exit 1 before assembly (the test); BEARBROWSER_COCKPIT_RES override resolves the staged app and stands up the governed topology (cockpit→gate→sidecar + receipts). Full brew install cycle for you to run: brew install --formula packaging/homebrew/Formula/bearbrowser.rb # runs the test brew install node bun && bearbrowser-cockpit-assemble && bearbrowser-cockpit-up --- packaging/homebrew/Formula/bearbrowser.rb | 24 +++++++++++++++++++++++ scripts/bearbrowser-cockpit-up | 5 ++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packaging/homebrew/Formula/bearbrowser.rb b/packaging/homebrew/Formula/bearbrowser.rb index 2c9f73b..693cbcf 100644 --- a/packaging/homebrew/Formula/bearbrowser.rb +++ b/packaging/homebrew/Formula/bearbrowser.rb @@ -32,6 +32,23 @@ def install (bin/"bearbrowser-verify-sidecar-status").write wrapper_for("verify-sidecar-status.sh") (bin/"bearbrowser-verify-interactive-sidecar").write wrapper_for("verify-interactive-sidecar.sh") (bin/"bearbrowser-verify-agent-sidecar").write wrapper_for("verify-agent-sidecar-contract.py") + + # Sovereign cockpit — assemble (build client-vue + the agent-machine sidecar binary) + # then run it, governed. The Cellar is read-only, so both target a writable user data + # dir. `assemble` needs node + bun (see caveats); it checks and guides if they're absent. + cockpit_res = '${XDG_DATA_HOME:-$HOME/.local/share}/bearbrowser/cockpit-app/Contents/Resources' + (bin/"bearbrowser-cockpit-assemble").write <<~EOS + #!/usr/bin/env bash + set -euo pipefail + export STAGE="#{cockpit_res}" + exec bash "#{libexec}/scripts/assemble-cockpit.sh" "$@" + EOS + (bin/"bearbrowser-cockpit-up").write <<~EOS + #!/usr/bin/env bash + set -euo pipefail + export BEARBROWSER_COCKPIT_RES="#{cockpit_res}" + exec bash "#{libexec}/scripts/bearbrowser-cockpit-up" "$@" + EOS (bin/"bearbrowser-verify-native-shell").write wrapper_for("verify-native-macos-shell.sh") (bin/"bearbrowser-verify-control-plane").write wrapper_for("verify-sourceos-control-plane.py") (bin/"bearbrowser-build-binary").write wrapper_for("bearbrowser-build-binary.sh") @@ -94,6 +111,11 @@ def caveats bearbrowser-verify-agent-sidecar bearbrowser-verify-native-shell bearbrowser-verify-control-plane + + Sovereign cockpit (one governed unit; needs: brew install node bun): + bearbrowser-cockpit-assemble # build client-vue + agent-machine → ~/.local/share/bearbrowser + bearbrowser-cockpit-up # run governed: cockpit → gate → agent-machine (+ receipts), loopback only + bearbrowser --profile agent-runtime --ref latest --dry-run bearbrowser-build-binary --profile agent-runtime --dry-run bearbrowser-verify-build-lane @@ -141,6 +163,8 @@ def caveats assert_match "BearBrowser sidecar status verified", shell_output("#{bin}/bearbrowser-verify-sidecar-status") assert_match "BearBrowser SourceOS control-plane manifests verified", shell_output("#{bin}/bearbrowser-verify-control-plane") assert_match "http://127.0.0.1:", shell_output("#{bin}/bearbrowser-sidecar-server --print-url") + # cockpit CLIs install + fail cleanly before assembly (no node/bun needed to prove the wiring). + assert_match "run assemble-cockpit.sh", shell_output("#{bin}/bearbrowser-cockpit-up 2>&1", 1) assert_match "bearhistory-policy-explain", shell_output("#{bin}/bearbrowser-history policy explain --profile agent-runtime --dry-run") assert_match "bearhistory-export-explain", shell_output("#{bin}/bearbrowser-history export explain --session demo --profile agent-runtime --dry-run") end diff --git a/scripts/bearbrowser-cockpit-up b/scripts/bearbrowser-cockpit-up index cd91999..e186d59 100755 --- a/scripts/bearbrowser-cockpit-up +++ b/scripts/bearbrowser-cockpit-up @@ -16,7 +16,10 @@ set -uo pipefail SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -RES="$(cd "$SELF_DIR/.." && pwd)" # …/Contents/Resources +# RES = the assembled Contents/Resources. Defaults to the script's parent (dev/staging +# layout); a package install sets BEARBROWSER_COCKPIT_RES to the writable user data dir +# that `assemble-cockpit.sh` built into (libexec is read-only, so the app can't live there). +RES="${BEARBROWSER_COCKPIT_RES:-$(cd "$SELF_DIR/.." && pwd)}" BIN="$RES/sidecars/bearbrowser-agent-machine-bin" GATE="$SELF_DIR/bearbrowser-agent-machine-gate.py" RECEIPTS="$SELF_DIR/bearbrowser-receipts.py"