baa-conductor


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

start-launchd.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/start-launchd.sh [options]
 12
 13Options:
 14  --scope agent|daemon   launchd domain type. Defaults to agent.
 15  --service NAME         Add one service to the start set. Repeatable.
 16  --all-services         Start conductor, codexd, worker-runner, and status-api.
 17  --home-dir PATH        Used only to derive the default LaunchAgents path.
 18  --install-dir PATH     Override launchd install directory.
 19  --domain TARGET        Override launchctl domain target. Defaults to gui/<uid> or system.
 20  --dry-run              Print launchctl commands instead of executing them.
 21  --help                 Show this help text.
 22
 23Notes:
 24  If no service is specified, conductor + codexd are started.
 25  Use --service status-api to start the optional local read-only observer.
 26EOF
 27}
 28
 29require_command launchctl
 30require_command plutil
 31
 32scope="agent"
 33home_dir="$(default_home_dir)"
 34install_dir=""
 35domain_target=""
 36dry_run="0"
 37services=()
 38
 39while [[ $# -gt 0 ]]; do
 40  case "$1" in
 41    --scope)
 42      scope="$2"
 43      shift 2
 44      ;;
 45    --service)
 46      validate_service "$2"
 47      if ! contains_value "$2" "${services[@]-}"; then
 48        services+=("$2")
 49      fi
 50      shift 2
 51      ;;
 52    --all-services)
 53      while IFS= read -r service; do
 54        if ! contains_value "$service" "${services[@]-}"; then
 55          services+=("$service")
 56        fi
 57      done < <(all_services)
 58      shift
 59      ;;
 60    --home-dir)
 61      home_dir="$2"
 62      shift 2
 63      ;;
 64    --install-dir)
 65      install_dir="$2"
 66      shift 2
 67      ;;
 68    --domain)
 69      domain_target="$2"
 70      shift 2
 71      ;;
 72    --dry-run)
 73      dry_run="1"
 74      shift
 75      ;;
 76    --help)
 77      usage
 78      exit 0
 79      ;;
 80    *)
 81      die "Unknown option: $1"
 82      ;;
 83  esac
 84done
 85
 86validate_scope "$scope"
 87
 88if [[ "${#services[@]}" -eq 0 ]]; then
 89  while IFS= read -r service; do
 90    services+=("$service")
 91  done < <(default_node_verification_services)
 92fi
 93
 94if [[ -z "$install_dir" ]]; then
 95  install_dir="$(default_install_dir "$scope" "$home_dir")"
 96fi
 97
 98if [[ -z "$domain_target" ]]; then
 99  domain_target="$(default_domain_target "$scope")"
100fi
101
102service_is_loaded() {
103  launchctl print "${domain_target}/$(service_label "$1")" >/dev/null 2>&1
104}
105
106for service in "${services[@]}"; do
107  plist_path="$(service_install_path "$install_dir" "$service")"
108  assert_file "$plist_path"
109  plutil -lint "$plist_path" >/dev/null
110
111  if service_is_loaded "$service"; then
112    runtime_log "${service} already loaded"
113    continue
114  fi
115
116  run_or_print "$dry_run" launchctl bootstrap "$domain_target" "$plist_path"
117  run_or_print "$dry_run" launchctl kickstart -k "${domain_target}/$(service_label "$service")"
118done
119
120runtime_log "launchd start completed for ${domain_target}"