xworkmate-app/scripts/codex-acp-service-common.sh

248 lines
5.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
XWORKMATE_CODEX_ACP_DEFAULT_HOST="${XWORKMATE_CODEX_ACP_DEFAULT_HOST:-127.0.0.1}"
XWORKMATE_CODEX_ACP_DEFAULT_PORT="${XWORKMATE_CODEX_ACP_DEFAULT_PORT:-9001}"
XWORKMATE_CODEX_ACP_PORT_SCAN_LIMIT="${XWORKMATE_CODEX_ACP_PORT_SCAN_LIMIT:-100}"
XWORKMATE_CODEX_ACP_DRY_RUN="${XWORKMATE_CODEX_ACP_DRY_RUN:-0}"
common_die() {
echo "Error: $*" >&2
exit 1
}
common_info() {
echo "$*"
}
common_warn() {
echo "Warning: $*" >&2
}
common_print_cmd() {
printf '+'
printf ' %q' "$@"
printf '\n'
}
common_run_service_cmd() {
if [[ "$XWORKMATE_CODEX_ACP_DRY_RUN" == "1" ]]; then
common_print_cmd "$@"
return 0
fi
"$@"
}
common_run_service_cmd_allow_fail() {
if [[ "$XWORKMATE_CODEX_ACP_DRY_RUN" == "1" ]]; then
common_print_cmd "$@"
return 0
fi
"$@" || true
}
common_validate_port() {
local port="$1"
if [[ ! "$port" =~ ^[0-9]+$ ]]; then
common_die "Invalid port: $port"
fi
if (( port < 1 || port > 65535 )); then
common_die "Port out of range: $port"
fi
}
common_resolve_codex_bin() {
local candidate="${CODEX_BIN:-${XWORKMATE_CODEX_ACP_CODEX_BIN:-}}"
if [[ -z "$candidate" ]]; then
candidate="$(command -v codex || true)"
fi
if [[ -z "$candidate" ]]; then
common_die "Unable to find codex in PATH. Set CODEX_BIN=/absolute/path/to/codex."
fi
if [[ ! -x "$candidate" ]]; then
common_die "codex binary is not executable: $candidate"
fi
printf '%s\n' "$candidate"
}
common_listen_url() {
local port="$1"
printf 'ws://%s:%s\n' "$XWORKMATE_CODEX_ACP_DEFAULT_HOST" "$port"
}
common_load_config() {
if [[ -f "$CONFIG_FILE" ]]; then
# shellcheck disable=SC1090
source "$CONFIG_FILE"
fi
}
common_ensure_dirs() {
mkdir -p "$CONFIG_DIR" "$RUNTIME_DIR"
if [[ -n "${LOG_DIR:-}" ]]; then
mkdir -p "$LOG_DIR"
fi
}
common_write_config() {
local codex_bin="$1"
local port="$2"
local listen_url
listen_url="$(common_listen_url "$port")"
common_ensure_dirs
{
printf 'CODEX_BIN=%q\n' "$codex_bin"
printf 'CODEX_ACP_HOST=%q\n' "$XWORKMATE_CODEX_ACP_DEFAULT_HOST"
printf 'CODEX_ACP_PORT=%q\n' "$port"
printf 'CODEX_ACP_LISTEN_URL=%q\n' "$listen_url"
} > "$CONFIG_FILE"
}
common_write_launcher() {
local codex_bin="$1"
local listen_url="$2"
common_ensure_dirs
{
printf '#!/usr/bin/env bash\n'
printf 'set -euo pipefail\n'
printf 'exec '
printf '%q ' "$codex_bin" "app-server" "--listen" "$listen_url"
printf '\n'
} > "$LAUNCHER_FILE"
chmod +x "$LAUNCHER_FILE"
}
common_port_is_in_use() {
local port="$1"
if command -v lsof >/dev/null 2>&1; then
lsof -nP -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1
return $?
fi
if command -v ss >/dev/null 2>&1; then
ss -H -ltn 2>/dev/null | awk -v needle=":$port" '
$4 ~ needle "$" { found = 1 }
END { exit found ? 0 : 1 }
'
return $?
fi
if command -v netstat >/dev/null 2>&1; then
netstat -an 2>/dev/null | awk -v needle=":$port" '
$1 ~ /^tcp/ && $4 ~ needle "$" && $NF ~ /LISTEN|LISTENING/ { found = 1 }
END { exit found ? 0 : 1 }
'
return $?
fi
if command -v nc >/dev/null 2>&1; then
nc -z "$XWORKMATE_CODEX_ACP_DEFAULT_HOST" "$port" >/dev/null 2>&1
return $?
fi
common_die "Unable to detect port availability. Install one of lsof, ss, netstat, or nc."
}
common_find_available_port() {
local preferred="$1"
local port
local max_port=$((preferred + XWORKMATE_CODEX_ACP_PORT_SCAN_LIMIT))
if (( max_port > 65535 )); then
max_port=65535
fi
for ((port = preferred; port <= max_port; port++)); do
if ! common_port_is_in_use "$port"; then
printf '%s\n' "$port"
return 0
fi
done
common_die "No free port found in range ${preferred}-${max_port}."
}
common_select_port() {
local preferred="$1"
local current_port="${2:-}"
local preserve_current_busy="${3:-0}"
common_validate_port "$preferred"
if [[ "$preserve_current_busy" == "1" && -n "$current_port" && "$preferred" == "$current_port" ]]; then
printf '%s\n' "$preferred"
return 0
fi
if common_port_is_in_use "$preferred"; then
common_find_available_port "$preferred"
return 0
fi
printf '%s\n' "$preferred"
}
common_endpoint_from_config() {
common_load_config
local port="${CODEX_ACP_PORT:-$XWORKMATE_CODEX_ACP_DEFAULT_PORT}"
common_validate_port "$port"
common_listen_url "$port"
}
common_cleanup_dir_if_empty() {
local dir="$1"
if [[ -d "$dir" ]]; then
rmdir "$dir" 2>/dev/null || true
fi
}
common_parse_args() {
ACTION="${1:-help}"
shift || true
PORT_OVERRIDE=""
LOG_LINES="50"
while [[ $# -gt 0 ]]; do
case "$1" in
--port)
shift
[[ $# -gt 0 ]] || common_die "--port requires a value"
PORT_OVERRIDE="$1"
shift
;;
--lines)
shift
[[ $# -gt 0 ]] || common_die "--lines requires a value"
LOG_LINES="$1"
shift
;;
-h|--help|help)
ACTION="help"
shift
;;
*)
if [[ "$ACTION" == "set-port" && -z "$PORT_OVERRIDE" ]]; then
PORT_OVERRIDE="$1"
shift
continue
fi
common_die "Unknown argument: $1"
;;
esac
done
if [[ -n "$PORT_OVERRIDE" ]]; then
common_validate_port "$PORT_OVERRIDE"
fi
if [[ ! "$LOG_LINES" =~ ^[0-9]+$ ]]; then
common_die "Invalid --lines value: $LOG_LINES"
fi
}