baa-conductor


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

common.sh

  1#!/usr/bin/env bash
  2
  3if [[ -n "${BAA_RUNTIME_COMMON_SH_LOADED:-}" ]]; then
  4  return 0
  5fi
  6
  7readonly BAA_RUNTIME_COMMON_SH_LOADED=1
  8readonly BAA_RUNTIME_SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
  9readonly BAA_RUNTIME_REPO_DIR_DEFAULT="$(cd -- "${BAA_RUNTIME_SCRIPT_DIR}/../.." && pwd)"
 10readonly BAA_RUNTIME_DEFAULT_PUBLIC_API_BASE="https://conductor.makefile.so"
 11readonly BAA_RUNTIME_DEFAULT_CONTROL_API_BASE="${BAA_RUNTIME_DEFAULT_PUBLIC_API_BASE}"
 12readonly BAA_RUNTIME_DEFAULT_LOCAL_API="http://127.0.0.1:4317"
 13readonly BAA_RUNTIME_DEFAULT_CODEXD_LOCAL_API="http://127.0.0.1:4319"
 14readonly BAA_RUNTIME_DEFAULT_CODEXD_EVENT_STREAM_PATH="/v1/codexd/events"
 15readonly BAA_RUNTIME_DEFAULT_CODEXD_SERVER_MODE="app-server"
 16readonly BAA_RUNTIME_DEFAULT_CODEXD_SERVER_STRATEGY="spawn"
 17readonly BAA_RUNTIME_DEFAULT_CODEXD_SERVER_COMMAND="codex"
 18readonly BAA_RUNTIME_DEFAULT_CODEXD_SERVER_ARGS="app-server"
 19readonly BAA_RUNTIME_DEFAULT_CODEXD_SERVER_ENDPOINT="stdio://codex-app-server"
 20readonly BAA_RUNTIME_DEFAULT_CLAUDE_CODED_LOCAL_API="http://127.0.0.1:4320"
 21readonly BAA_RUNTIME_DEFAULT_STATUS_API="http://127.0.0.1:4318"
 22readonly BAA_RUNTIME_DEFAULT_LOCALE="en_US.UTF-8"
 23
 24runtime_log() {
 25  printf '[runtime] %s\n' "$*"
 26}
 27
 28runtime_error() {
 29  printf '[runtime] error: %s\n' "$*" >&2
 30}
 31
 32die() {
 33  runtime_error "$*"
 34  exit 1
 35}
 36
 37require_command() {
 38  if ! command -v "$1" >/dev/null 2>&1; then
 39    die "Missing required command: $1"
 40  fi
 41}
 42
 43contains_value() {
 44  local needle="$1"
 45  shift
 46
 47  local value
 48  for value in "$@"; do
 49    if [[ "$value" == "$needle" ]]; then
 50      return 0
 51    fi
 52  done
 53
 54  return 1
 55}
 56
 57validate_service() {
 58  case "$1" in
 59    conductor | codexd | claude-coded | worker-runner | status-api) ;;
 60    *)
 61      die "Unsupported service: $1"
 62      ;;
 63  esac
 64}
 65
 66validate_scope() {
 67  case "$1" in
 68    agent | daemon) ;;
 69    *)
 70      die "Unsupported launchd scope: $1"
 71      ;;
 72  esac
 73}
 74
 75validate_node() {
 76  case "$1" in
 77    mini) ;;
 78    *)
 79      die "Unsupported node: $1"
 80      ;;
 81  esac
 82}
 83
 84default_services() {
 85  printf '%s\n' conductor codexd
 86}
 87
 88default_node_verification_services() {
 89  printf '%s\n' conductor codexd
 90}
 91
 92all_services() {
 93  printf '%s\n' conductor codexd claude-coded worker-runner status-api
 94}
 95
 96service_requires_shared_token() {
 97  case "$1" in
 98    codexd | claude-coded)
 99      return 1
100      ;;
101    *)
102      return 0
103      ;;
104  esac
105}
106
107service_uses_public_api_base() {
108  case "$1" in
109    conductor)
110      return 0
111      ;;
112    *)
113      return 1
114      ;;
115  esac
116}
117
118service_uses_control_api_base() {
119  service_uses_public_api_base "$1"
120}
121
122service_label() {
123  case "$1" in
124    conductor)
125      printf '%s\n' "so.makefile.baa-conductor"
126      ;;
127    codexd)
128      printf '%s\n' "so.makefile.baa-codexd"
129      ;;
130    claude-coded)
131      printf '%s\n' "so.makefile.baa-claude-coded"
132      ;;
133    worker-runner)
134      printf '%s\n' "so.makefile.baa-worker-runner"
135      ;;
136    status-api)
137      printf '%s\n' "so.makefile.baa-status-api"
138      ;;
139  esac
140}
141
142service_dist_entry_relative() {
143  case "$1" in
144    conductor)
145      printf '%s\n' "apps/conductor-daemon/dist/index.js"
146      ;;
147    codexd)
148      printf '%s\n' "apps/codexd/dist/index.js"
149      ;;
150    claude-coded)
151      printf '%s\n' "apps/claude-coded/dist/index.js"
152      ;;
153    worker-runner)
154      printf '%s\n' "apps/worker-runner/dist/index.js"
155      ;;
156    status-api)
157      printf '%s\n' "apps/status-api/dist/index.js"
158      ;;
159  esac
160}
161
162service_default_port() {
163  case "$1" in
164    conductor)
165      printf '%s\n' "4317"
166      ;;
167    codexd)
168      printf '%s\n' "4319"
169      ;;
170    claude-coded)
171      printf '%s\n' "4320"
172      ;;
173    status-api)
174      printf '%s\n' "4318"
175      ;;
176    worker-runner)
177      printf '%s\n' ""
178      ;;
179  esac
180}
181
182service_process_match() {
183  local repo_dir="$1"
184  local service="$2"
185  local conductor_host="${3:-}"
186  local conductor_role="${4:-}"
187  local dist_entry
188
189  dist_entry="${repo_dir}/$(service_dist_entry_relative "$service")"
190
191  case "$service" in
192    conductor)
193      printf '%s --host %s --role %s\n' "$dist_entry" "$conductor_host" "$conductor_role"
194      ;;
195    codexd)
196      printf '%s start\n' "$dist_entry"
197      ;;
198    claude-coded)
199      printf '%s start\n' "$dist_entry"
200      ;;
201    *)
202      printf '%s\n' "$dist_entry"
203      ;;
204  esac
205}
206
207service_template_path() {
208  local repo_dir="$1"
209  local service="$2"
210
211  printf '%s/ops/launchd/%s.plist\n' "$repo_dir" "$(service_label "$service")"
212}
213
214service_install_path() {
215  local install_dir="$1"
216  local service="$2"
217
218  printf '%s/%s.plist\n' "$install_dir" "$(service_label "$service")"
219}
220
221service_stdout_path() {
222  local logs_launchd_dir="$1"
223  local service="$2"
224
225  printf '%s/%s.out.log\n' "$logs_launchd_dir" "$(service_label "$service")"
226}
227
228service_stderr_path() {
229  local logs_launchd_dir="$1"
230  local service="$2"
231
232  printf '%s/%s.err.log\n' "$logs_launchd_dir" "$(service_label "$service")"
233}
234
235resolve_node_defaults() {
236  case "$1" in
237    mini)
238      printf '%s %s %s\n' "mini" "primary" "mini-main"
239      ;;
240  esac
241}
242
243default_home_dir() {
244  if [[ -n "${HOME:-}" ]]; then
245    printf '%s\n' "$HOME"
246    return 0
247  fi
248
249  printf '/Users/%s\n' "$(id -un)"
250}
251
252default_username() {
253  printf '%s\n' "$(id -un)"
254}
255
256default_launchd_path() {
257  local home_dir="$1"
258
259  printf '%s\n' "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:${home_dir}/.local/bin:${home_dir}/bin"
260}
261
262default_install_dir() {
263  local scope="$1"
264  local home_dir="$2"
265
266  case "$scope" in
267    agent)
268      printf '%s/Library/LaunchAgents\n' "$home_dir"
269      ;;
270    daemon)
271      printf '%s\n' "/Library/LaunchDaemons"
272      ;;
273  esac
274}
275
276default_domain_target() {
277  local scope="$1"
278
279  case "$scope" in
280    agent)
281      printf 'gui/%s\n' "$(id -u)"
282      ;;
283    daemon)
284      printf '%s\n' "system"
285      ;;
286  esac
287}
288
289ensure_directory() {
290  local path="$1"
291  local mode="$2"
292
293  install -d -m "$mode" "$path"
294}
295
296assert_directory() {
297  if [[ ! -d "$1" ]]; then
298    die "Missing directory: $1"
299  fi
300}
301
302assert_file() {
303  if [[ ! -f "$1" ]]; then
304    die "Missing file: $1"
305  fi
306}
307
308escape_plist_value() {
309  local value="$1"
310
311  value=${value//\\/\\\\}
312  value=${value//\"/\\\"}
313
314  printf '%s' "$value"
315}
316
317plist_set_string() {
318  local plist_path="$1"
319  local key="$2"
320  local value
321
322  value="$(escape_plist_value "$3")"
323
324  if ! /usr/libexec/PlistBuddy -c "Set ${key} \"${value}\"" "$plist_path" >/dev/null 2>&1; then
325    /usr/libexec/PlistBuddy -c "Add ${key} string \"${value}\"" "$plist_path" >/dev/null
326  fi
327}
328
329plist_delete_key() {
330  local plist_path="$1"
331  local key="$2"
332
333  /usr/libexec/PlistBuddy -c "Delete ${key}" "$plist_path" >/dev/null 2>&1 || true
334}
335
336plist_print_value() {
337  local plist_path="$1"
338  local key="$2"
339
340  /usr/libexec/PlistBuddy -c "Print ${key}" "$plist_path"
341}
342
343plist_has_key() {
344  local plist_path="$1"
345  local key="$2"
346
347  /usr/libexec/PlistBuddy -c "Print ${key}" "$plist_path" >/dev/null 2>&1
348}
349
350print_shell_command() {
351  printf '+'
352
353  local arg
354  for arg in "$@"; do
355    printf ' %q' "$arg"
356  done
357
358  printf '\n'
359}
360
361run_or_print() {
362  local dry_run="$1"
363  shift
364
365  if [[ "$dry_run" == "1" ]]; then
366    print_shell_command "$@"
367    return 0
368  fi
369
370  "$@"
371}
372
373resolve_runtime_paths() {
374  local repo_dir="$1"
375
376  printf '%s\n' \
377    "${repo_dir}/state" \
378    "${repo_dir}/state/codexd" \
379    "${repo_dir}/runs" \
380    "${repo_dir}/worktrees" \
381    "${repo_dir}/logs" \
382    "${repo_dir}/logs/codexd" \
383    "${repo_dir}/logs/launchd" \
384    "${repo_dir}/tmp"
385}
386
387load_shared_token() {
388  local shared_token="$1"
389  local shared_token_file="$2"
390
391  if [[ -n "$shared_token_file" ]]; then
392    assert_file "$shared_token_file"
393    shared_token="$(tr -d '\r\n' <"$shared_token_file")"
394  fi
395
396  printf '%s' "$shared_token"
397}
398
399read_env_file_value() {
400  local env_file="$1"
401  local key="$2"
402
403  if [[ -z "$env_file" || ! -f "$env_file" ]]; then
404    return 0
405  fi
406
407  awk -F= -v key="$key" '
408    $1 == key {
409      sub(/^[^=]*=/, "");
410      print;
411      exit;
412    }
413  ' "$env_file"
414}
415
416resolve_env_or_file_value() {
417  local current_value="$1"
418  local key="$2"
419  local env_file="$3"
420
421  if [[ -n "$current_value" ]]; then
422    printf '%s' "$current_value"
423    return 0
424  fi
425
426  read_env_file_value "$env_file" "$key"
427}