baa-conductor


baa-conductor / scripts / runtime
im_wower  ·  2026-03-29

install-mini.sh

  1#!/usr/bin/env bash
  2set -euo pipefail
  3
  4SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
  5# shellcheck source=./common.sh
  6source "${SCRIPT_DIR}/common.sh"
  7
  8usage() {
  9  cat <<'EOF'
 10Usage:
 11  scripts/runtime/install-mini.sh [options]
 12
 13Options:
 14  --repo-dir PATH           Repo root. Defaults to the current checkout.
 15  --home-dir PATH           HOME used for LaunchAgents and defaults.
 16  --install-dir PATH        Override launchd install directory.
 17  --shared-token-file PATH  Preferred shared token file. Defaults to ~/.config/baa-conductor/shared-token.txt
 18  --secrets-env PATH        Preferred env fallback used to extract BAA_SHARED_TOKEN.
 19                            Defaults to ~/.config/baa-conductor/runtime-secrets.env.
 20                            Falls back to legacy
 21                            ~/.config/baa-conductor/control-api-worker.secrets.env
 22                            only if the default file is missing.
 23                            If the file also defines D1_ACCOUNT_ID,
 24                            D1_DATABASE_ID, and CLOUDFLARE_API_TOKEN, the
 25                            conductor LaunchAgent will carry D1 sync config.
 26  --with-status-api         Also install/restart/check the optional local status-api service.
 27  --skip-build              Skip pnpm build.
 28  --skip-restart            Skip launchd restart.
 29  --skip-check              Skip check-launchd/check-node verification.
 30  --help                    Show this help text.
 31
 32Notes:
 33  This is the single-node mini convenience wrapper. It:
 34  1. bootstraps runtime directories
 35  2. builds the repo
 36  3. installs conductor + codexd LaunchAgents by default
 37  4. restarts them
 38  5. verifies the node
 39  Add --with-status-api when you also want the optional local read-only observer.
 40  codexd verification only treats /healthz and /v1/codexd/status as install acceptance;
 41  /v1/codexd/runs* and codex exec are not part of the formal runtime contract.
 42EOF
 43}
 44
 45require_command awk
 46require_command chmod
 47require_command curl
 48require_command mkdir
 49require_command npx
 50
 51repo_dir="${BAA_RUNTIME_REPO_DIR_DEFAULT}"
 52home_dir="$(default_home_dir)"
 53install_dir=""
 54shared_token_file=""
 55secrets_env=""
 56skip_build="0"
 57skip_restart="0"
 58skip_check="0"
 59with_status_api="0"
 60codexd_api_base="${BAA_RUNTIME_DEFAULT_CODEXD_LOCAL_API}"
 61status_api_base="http://100.71.210.78:4318"
 62
 63while [[ $# -gt 0 ]]; do
 64  case "$1" in
 65    --repo-dir)
 66      repo_dir="$2"
 67      shift 2
 68      ;;
 69    --home-dir)
 70      home_dir="$2"
 71      shift 2
 72      ;;
 73    --install-dir)
 74      install_dir="$2"
 75      shift 2
 76      ;;
 77    --shared-token-file)
 78      shared_token_file="$2"
 79      shift 2
 80      ;;
 81    --secrets-env)
 82      secrets_env="$2"
 83      shift 2
 84      ;;
 85    --skip-build)
 86      skip_build="1"
 87      shift
 88      ;;
 89    --with-status-api)
 90      with_status_api="1"
 91      shift
 92      ;;
 93    --skip-restart)
 94      skip_restart="1"
 95      shift
 96      ;;
 97    --skip-check)
 98      skip_check="1"
 99      shift
100      ;;
101    --help)
102      usage
103      exit 0
104      ;;
105    *)
106      die "Unknown option: $1"
107      ;;
108  esac
109done
110
111if [[ -z "$install_dir" ]]; then
112  install_dir="$(default_install_dir agent "$home_dir")"
113fi
114
115if [[ -z "$shared_token_file" ]]; then
116  shared_token_file="${home_dir}/.config/baa-conductor/shared-token.txt"
117fi
118
119if [[ -z "$secrets_env" ]]; then
120  secrets_env="${home_dir}/.config/baa-conductor/runtime-secrets.env"
121fi
122
123legacy_secrets_env="${home_dir}/.config/baa-conductor/control-api-worker.secrets.env"
124
125prepare_shared_token_file() {
126  local target_path="$1"
127  local env_file="$2"
128  local legacy_env_file="$3"
129  local token=""
130  local candidate_env_file=""
131
132  if [[ -f "$target_path" ]]; then
133    token="$(tr -d '\r\n' <"$target_path")"
134  fi
135
136  if [[ -z "$token" ]]; then
137    for candidate_env_file in "$env_file" "$legacy_env_file"; do
138      if [[ -z "$candidate_env_file" || ! -f "$candidate_env_file" ]]; then
139        continue
140      fi
141
142      token="$(awk -F= '/^BAA_SHARED_TOKEN=/{sub(/^[^=]*=/, ""); print; exit}' "$candidate_env_file")"
143      if [[ -n "$token" ]]; then
144        if [[ "$candidate_env_file" == "$legacy_env_file" ]]; then
145          runtime_log "using legacy secrets env fallback: ${legacy_env_file}"
146        fi
147        break
148      fi
149    done
150  fi
151
152  if [[ -z "$token" ]]; then
153    die "Could not resolve BAA_SHARED_TOKEN. Provide ${target_path}, ${env_file}, or ${legacy_env_file}."
154  fi
155
156  ensure_directory "$(dirname "$target_path")" "700"
157  printf '%s\n' "$token" >"$target_path"
158  chmod 600 "$target_path"
159}
160
161prepare_shared_token_file "$shared_token_file" "$secrets_env" "$legacy_secrets_env"
162
163wait_for_http() {
164  local name="$1"
165  local url="$2"
166  local attempts="${3:-30}"
167  local delay="${4:-1}"
168  local index
169
170  for ((index = 1; index <= attempts; index += 1)); do
171    if curl -fsS "$url" >/dev/null 2>&1; then
172      runtime_log "${name} is ready: ${url}"
173      return 0
174    fi
175
176    sleep "$delay"
177  done
178
179  die "${name} did not become ready in time: ${url}"
180}
181
182run_or_print 0 "${SCRIPT_DIR}/bootstrap.sh" --repo-dir "$repo_dir"
183
184if [[ "$skip_build" != "1" ]]; then
185  (
186    cd "$repo_dir"
187    npx --yes pnpm -r build
188  )
189fi
190
191install_services=(
192  --service conductor
193  --service codexd
194)
195
196if [[ "$with_status_api" == "1" ]]; then
197  install_services+=(--service status-api)
198fi
199
200run_or_print 0 "${SCRIPT_DIR}/install-launchd.sh" \
201  --repo-dir "$repo_dir" \
202  --node mini \
203  "${install_services[@]}" \
204  --install-dir "$install_dir" \
205  --shared-token-file "$shared_token_file" \
206  --d1-secrets-env "$secrets_env" \
207  --local-api-base "http://100.71.210.78:4317" \
208  --local-api-allowed-hosts "100.71.210.78" \
209  --codexd-local-api-base "$codexd_api_base" \
210  --status-api-host "100.71.210.78"
211
212if [[ "$skip_restart" != "1" ]]; then
213  run_or_print 0 "${SCRIPT_DIR}/restart-launchd.sh" \
214    --install-dir "$install_dir" \
215    "${install_services[@]}"
216fi
217
218if [[ "$skip_check" != "1" ]]; then
219  wait_for_http "conductor" "http://100.71.210.78:4317/healthz"
220  wait_for_http "codexd" "${codexd_api_base}/healthz"
221
222  if [[ "$with_status_api" == "1" ]]; then
223    wait_for_http "status-api" "${status_api_base}/healthz"
224  fi
225
226  run_or_print 0 "${SCRIPT_DIR}/check-launchd.sh" \
227    --repo-dir "$repo_dir" \
228    --node mini \
229    "${install_services[@]}" \
230    --install-dir "$install_dir" \
231    --shared-token-file "$shared_token_file" \
232    --d1-secrets-env "$secrets_env" \
233    --local-api-base "http://100.71.210.78:4317" \
234    --local-api-allowed-hosts "100.71.210.78" \
235    --codexd-local-api-base "$codexd_api_base" \
236    --status-api-host "100.71.210.78" \
237    --check-loaded
238
239  run_or_print 0 "${SCRIPT_DIR}/check-node.sh" \
240    --repo-dir "$repo_dir" \
241    --node mini \
242    "${install_services[@]}" \
243    --install-dir "$install_dir" \
244    --shared-token-file "$shared_token_file" \
245    --local-api-base "http://100.71.210.78:4317" \
246    --local-api-allowed-hosts "100.71.210.78" \
247    --codexd-api-base "$codexd_api_base" \
248    --status-api-base "${status_api_base}" \
249    --status-api-host "100.71.210.78" \
250    --expected-rolez leader \
251    --check-loaded
252fi
253
254runtime_log "mini runtime install completed"