From b16a9c0ed2a2a3a94897583d257d1beb3cbc2dc6 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 5 Sep 2026 04:18:41 +0000 Subject: [PATCH] Fix Codex startup approvals and companion upgrades --- .../skills/codex-app-server-upgrade/SKILL.md | 16 +- .../agents/openai.yaml | 4 +- .env.example | 2 + README.md | 2 +- scripts/start-codex-app-server | 209 +++++++++++++++--- 5 files changed, 192 insertions(+), 41 deletions(-) diff --git a/.codex/skills/codex-app-server-upgrade/SKILL.md b/.codex/skills/codex-app-server-upgrade/SKILL.md index 737298a..939b64c 100644 --- a/.codex/skills/codex-app-server-upgrade/SKILL.md +++ b/.codex/skills/codex-app-server-upgrade/SKILL.md @@ -1,6 +1,6 @@ --- name: codex-app-server-upgrade -description: Safely check, update, or self-upgrade the host Codex app-server and Codex CLI binary for this project. Use when asked to upgrade Codex, run app-server update checks, restart Codex after an update, or let Codex invoke its own app-server upgrade through the project script. +description: Safely check, update, or self-upgrade the host Codex app-server, Codex CLI binary, and its code-mode host for this project. Use when asked to upgrade Codex, repair or update its code-mode host, run app-server update checks, restart Codex after an update, or let Codex invoke its own app-server upgrade through the project script. --- # Codex App Server Upgrade @@ -19,6 +19,14 @@ If the current directory is not the project root, find the nearest repository co ## Commands +Start with automatic approval review when needed: + +```bash +scripts/start-codex-app-server start --approve-for-me +``` + +For a durable setting across restarts and upgrades, set `CODEX_APPROVE_FOR_ME=1` in `.env`. + Check status: ```bash @@ -41,7 +49,9 @@ scripts/start-codex-app-server check-updates -y ## Self-Upgrade Behavior -When the app-server is running, `check-updates -y` downloads and validates the new Codex binary first, then starts a detached worker to stop the app-server process group, replace the binary, and start the app-server again. This is the correct path even when Codex itself initiated the command. +When the app-server is running, `check-updates -y` downloads and validates matching Codex, code-mode host, and Linux `bwrap` binaries from the same release first. It then starts a detached worker to stop the app-server process group, replace all downloaded components, and start the app-server again. Release archives may contain target-suffixed executable names; the installer normalizes them to their stable installed paths. + +If Codex is already current but its sibling `codex-code-mode-host` binary is missing, `check-updates` downloads and installs the checked companion binary. On Linux it also repairs the bundled `bwrap` helper when needed. Expect the current Codex connection or tool call to be interrupted after the handoff. After a short delay, verify the outcome with: @@ -57,7 +67,7 @@ sed -n '1,160p' run/codex-app-server-upgrade.log ## Safety Rules -- Do not manually `kill`, `mv`, or overwrite the Codex binary for this workflow. +- Do not manually `kill`, `mv`, or overwrite the Codex or code-mode host binaries for this workflow. - Do not assume `/usr/local/bin/codex`, a specific home directory, or any machine-specific path. - Use `CODEX_BIN=/absolute/path/to/codex` only when the user or environment explicitly requires a non-default binary. - If the script reports that it cannot replace the binary without write permission, stop and report that non-interactive privileges or a writable `CODEX_BIN` are required. diff --git a/.codex/skills/codex-app-server-upgrade/agents/openai.yaml b/.codex/skills/codex-app-server-upgrade/agents/openai.yaml index 880bae1..373f627 100644 --- a/.codex/skills/codex-app-server-upgrade/agents/openai.yaml +++ b/.codex/skills/codex-app-server-upgrade/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "Codex App Server Upgrade" - short_description: "Safely self-upgrade Codex app-server." - default_prompt: "Safely check for and apply a Codex app-server upgrade using the project script." + short_description: "Safely upgrade Codex and its code-mode host." + default_prompt: "Use $codex-app-server-upgrade to safely update Codex and its code-mode host." diff --git a/.env.example b/.env.example index 08e92d9..5321932 100644 --- a/.env.example +++ b/.env.example @@ -18,6 +18,8 @@ DB_DIR=./data # Leave empty to use Codex defaults. DEFAULT_MODEL= DEFAULT_SANDBOX=workspace-write +# Set to 1 to route approval requests through Codex automatic review. +CODEX_APPROVE_FOR_ME=0 POLL_TIMEOUT_SECONDS=30 # Container should usually match the host user so SQLite files remain writable. diff --git a/README.md b/README.md index 2cdcc6e..5113085 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Docker Compose runs only the Go Telegram bot. Codex runs on the host through `co scripts/start-codex-app-server start ``` - The script supports `start`, `stop`, `status`, `check-updates [-y]`, and the alias `check-upgrade [-y]`. `start` launches Codex detached, writes `run/codex-app-server.pid`, logs to `run/codex-app-server.log`, and is idempotent if the socket is already live. `check-updates` compares the local `codex` binary with the latest OpenAI Codex GitHub release. With `-y`, it downloads and validates the matching platform archive before stopping a running app-server. If the app-server is running, the final stop/replace/start step is handed to a detached worker so the upgrade can complete even when invoked from Codex itself. If the upgraded server fails to start, the worker restores the previous binary and starts it again. + The script supports `start [--approve-for-me]`, `stop`, `status`, `check-updates [-y]`, and the alias `check-upgrade [-y]`. `start` launches Codex detached, writes `run/codex-app-server.pid`, logs to `run/codex-app-server.log`, and is idempotent if the socket is already live. `--approve-for-me` routes approval requests through Codex automatic review using the `workspace-write` sandbox. Set `CODEX_APPROVE_FOR_ME=1` in `.env` to retain that setting across restarts and upgrades. `check-updates` compares the local `codex` binary with the latest OpenAI Codex GitHub release. With `-y`, it downloads and validates the matching platform archives for Codex, the code-mode host, and Linux `bwrap` before stopping a running app-server. If the app-server is running, the final stop/replace/start step is handed to a detached worker so the upgrade can complete even when invoked from Codex itself. If the upgraded server fails to start, the worker restores the previous components and starts it again. 3. Add at least one Telegram user and workspace: diff --git a/scripts/start-codex-app-server b/scripts/start-codex-app-server index aef48a4..9bea8db 100755 --- a/scripts/start-codex-app-server +++ b/scripts/start-codex-app-server @@ -28,6 +28,7 @@ read_env_value() { HOST_CODEX_SOCKET="${HOST_CODEX_SOCKET:-$(read_env_value HOST_CODEX_SOCKET)}" HOST_CODEX_SOCKET="${HOST_CODEX_SOCKET:-$RUN_DIR/codex.sock}" +CODEX_APPROVE_FOR_ME="${CODEX_APPROVE_FOR_ME:-$(read_env_value CODEX_APPROVE_FOR_ME)}" mkdir -p "$RUN_DIR" chmod 700 "$RUN_DIR" @@ -37,15 +38,17 @@ usage() { Usage: $0 [options] Commands: - start Start codex app-server if it is not already running. + start [--approve-for-me] + Start codex app-server if it is not already running. stop Stop codex app-server and remove stale runtime files. status Print whether codex app-server is running. - check-updates [-y] Check GitHub releases and optionally install the latest Codex binary. + check-updates [-y] Check GitHub releases and optionally install Codex and its companion binaries. check-upgrade [-y] Alias for check-updates. Environment: CODEX_BIN Codex executable to replace. Defaults to the codex found on PATH. CODEX_RELEASE_REPO GitHub repo for releases. Defaults to openai/codex. + CODEX_APPROVE_FOR_ME Enable automatic approval review when starting Codex (1/true/yes). USAGE } @@ -114,7 +117,13 @@ codex_bin() { } start_server() { - local old_pid pid start_codex_bin + local old_pid pid start_codex_bin approve_for_me + approve_for_me="${1:-$CODEX_APPROVE_FOR_ME}" + case "$approve_for_me" in + 1|true|yes|on) approve_for_me=1 ;; + 0|false|no|off|"") approve_for_me=0 ;; + *) echo "invalid CODEX_APPROVE_FOR_ME value: $approve_for_me" >&2; return 2 ;; + esac old_pid="$(pid_from_file)" if [[ -n "$old_pid" ]] && kill -0 "$old_pid" 2>/dev/null; then if [[ -S "$HOST_CODEX_SOCKET" ]]; then @@ -145,8 +154,12 @@ start_server() { tail -f /dev/null > "$1" & writer=$! trap "kill $writer 2>/dev/null || true" EXIT - "$4" app-server --listen "$2" < "$1" - ' codex-app-server "$STDIN_FIFO" "unix://$HOST_CODEX_SOCKET" "$PID_FILE" "$start_codex_bin" >> "$LOG_FILE" 2>&1 + codex_args=(app-server --listen "$2") + if [[ "$5" == "1" ]]; then + codex_args=(--approve-for-me "${codex_args[@]}") + fi + "$4" "${codex_args[@]}" < "$1" + ' codex-app-server "$STDIN_FIFO" "unix://$HOST_CODEX_SOCKET" "$PID_FILE" "$start_codex_bin" "$approve_for_me" >> "$LOG_FILE" 2>&1 for _ in $(seq 1 50); do [[ -f "$PID_FILE" ]] && break @@ -312,6 +325,7 @@ target, path = sys.argv[1], sys.argv[2] needs_bwrap = "linux" in target codex_asset_name = f"codex-{target}.tar.gz" bwrap_asset_name = f"bwrap-{target}.tar.gz" if needs_bwrap else None +code_mode_host_asset_name = f"codex-code-mode-host-{target}.tar.gz" with open(path, "r", encoding="utf-8") as f: release = json.load(f) tag = release.get("tag_name", "") @@ -323,7 +337,11 @@ elif version.startswith("v"): assets = {asset.get("name"): asset for asset in release.get("assets", [])} codex_asset = assets.get(codex_asset_name) bwrap_asset = assets.get(bwrap_asset_name) if needs_bwrap else None -required_assets = [(codex_asset_name, codex_asset)] +code_mode_host_asset = assets.get(code_mode_host_asset_name) +required_assets = [ + (codex_asset_name, codex_asset), + (code_mode_host_asset_name, code_mode_host_asset), +] if needs_bwrap: required_assets.append((bwrap_asset_name, bwrap_asset)) missing = [name for name, asset in required_assets if asset is None] @@ -338,6 +356,8 @@ if bwrap_asset is not None: else: print("") print("") +print(code_mode_host_asset.get("browser_download_url", "")) +print(code_mode_host_asset.get("digest", "")) print(version) print(tag) PY @@ -399,6 +419,32 @@ extract_bwrap_binary() { printf '%s\n' "$found" } +extract_code_mode_host_binary() { + local archive="$1" dest="$2" found + local -a matches=() + mkdir -p "$dest/code-mode-host-extract" + tar -xzf "$archive" -C "$dest/code-mode-host-extract" + mapfile -d '' -t matches < <( + find "$dest/code-mode-host-extract" -type f \ + \( -name codex-code-mode-host -o -name 'codex-code-mode-host-*' \) -print0 + ) + if [[ "${#matches[@]}" -eq 0 ]]; then + echo "downloaded archive does not contain a code-mode host executable" >&2 + return 1 + fi + if [[ "${#matches[@]}" -ne 1 ]]; then + echo "downloaded archive contains multiple code-mode host executables" >&2 + return 1 + fi + found="${matches[0]}" + chmod +x "$found" + if ! "$found" --help >/dev/null 2>&1; then + echo "downloaded code-mode host executable failed validation" >&2 + return 1 + fi + printf '%s\n' "$found" +} + bundled_bwrap_path() { local bin="$1" printf '%s/codex-resources/bwrap\n' "$(dirname "$bin")" @@ -414,6 +460,17 @@ bwrap_required_for_target() { [[ "$1" == *linux* ]] } +code_mode_host_path() { + local bin="$1" + printf '%s/codex-code-mode-host\n' "$(dirname "$bin")" +} + +code_mode_host_installed() { + local bin="$1" host + host="$(code_mode_host_path "$bin")" + [[ -x "$host" && ! -L "$host" ]] +} + run_install() { if [[ "${#INSTALL_PREFIX[@]}" -gt 0 ]]; then "${INSTALL_PREFIX[@]}" "$@" @@ -456,8 +513,27 @@ install_bundled_bwrap() { run_install mv -f "$tmp_new" "$bundled" } +install_code_mode_host() { + local candidate="$1" bin="$2" backup="$3" host host_dir tmp_new host_backup host_missing + host="$(code_mode_host_path "$bin")" + host_dir="$(dirname "$host")" + tmp_new="$host.new.$$" + host_backup="$backup.code-mode-host" + host_missing="$backup.code-mode-host.missing" + + run_install mkdir -p "$host_dir" + if [[ -e "$host" ]]; then + run_install cp -p "$host" "$host_backup" + else + run_install touch "$host_missing" + fi + run_install install -m 0755 "$candidate" "$tmp_new" + run_install mv -f "$tmp_new" "$host" +} + install_candidate() { - local candidate="$1" bwrap_candidate="$2" bin="$3" backup="$4" tmp_new="$bin.new.$$" + local candidate="$1" bwrap_candidate="$2" code_mode_host_candidate="$3" bin="$4" backup="$5" tmp_new + tmp_new="$bin.new.$$" choose_install_prefix "$bin" run_install cp -p "$bin" "$backup" run_install install -m 0755 "$candidate" "$tmp_new" @@ -465,10 +541,11 @@ install_candidate() { if [[ -n "$bwrap_candidate" ]]; then install_bundled_bwrap "$bwrap_candidate" "$bin" "$backup" fi + install_code_mode_host "$code_mode_host_candidate" "$bin" "$backup" } restore_backup() { - local bin="$1" backup="$2" tmp_failed="$bin.failed.$$" bundled bwrap_backup bwrap_missing + local bin="$1" backup="$2" tmp_failed="$bin.failed.$$" bundled bwrap_backup bwrap_missing host host_backup host_missing if [[ ! -e "$backup" ]]; then echo "backup missing; cannot restore $bin" >&2 return 1 @@ -489,6 +566,17 @@ restore_backup() { run_install rm -f "$bundled" run_install rm -f "$bwrap_missing" fi + + host="$(code_mode_host_path "$bin")" + host_backup="$backup.code-mode-host" + host_missing="$backup.code-mode-host.missing" + if [[ -e "$host_backup" ]]; then + run_install mkdir -p "$(dirname "$host")" + run_install mv -f "$host_backup" "$host" + elif [[ -e "$host_missing" ]]; then + run_install rm -f "$host" + run_install rm -f "$host_missing" + fi } confirm_upgrade() { @@ -506,14 +594,15 @@ confirm_upgrade() { } apply_upgrade() { - local candidate="$1" bwrap_candidate="$2" bin="$3" backup="$4" local_version="$5" latest_version="$6" was_running=0 + local candidate="$1" bwrap_candidate="$2" code_mode_host_candidate="$3" bin="$4" backup="$5" local_version="$6" latest_version="$7" was_running=0 if is_running; then was_running=1 stop_server fi - if ! install_candidate "$candidate" "$bwrap_candidate" "$bin" "$backup"; then + if ! install_candidate "$candidate" "$bwrap_candidate" "$code_mode_host_candidate" "$bin" "$backup"; then echo "failed to install Codex update" >&2 + restore_backup "$bin" "$backup" || true if [[ "$was_running" == "1" ]]; then start_server || true fi @@ -534,12 +623,12 @@ apply_upgrade() { } handoff_upgrade() { - local candidate="$1" bwrap_candidate="$2" bin="$3" backup="$4" update_dir="$5" local_version="$6" latest_version="$7" + local candidate="$1" bwrap_candidate="$2" code_mode_host_candidate="$3" bin="$4" backup="$5" update_dir="$6" local_version="$7" latest_version="$8" : > "$UPGRADE_LOG_FILE" setsid -f bash -c ' sleep 1 - "$0" __apply-upgrade "$1" "$2" "$3" "$4" "$5" "$6" "$7" - ' "$0" "$candidate" "$bwrap_candidate" "$bin" "$backup" "$update_dir" "$local_version" "$latest_version" >> "$UPGRADE_LOG_FILE" 2>&1 + "$0" __apply-upgrade "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" + ' "$0" "$candidate" "$bwrap_candidate" "$code_mode_host_candidate" "$bin" "$backup" "$update_dir" "$local_version" "$latest_version" >> "$UPGRADE_LOG_FILE" 2>&1 echo "Codex upgrade handoff started; app-server will restart if replacement succeeds. log=$UPGRADE_LOG_FILE" } @@ -559,8 +648,8 @@ check_updates() { require_cmd python3 require_cmd ps - local bin local_version target json latest_version latest_tag codex_download_url codex_digest bwrap_download_url bwrap_digest - local codex_archive bwrap_archive tmp candidate bwrap_candidate candidate_version backup + local bin local_version target json latest_version latest_tag codex_download_url codex_digest bwrap_download_url bwrap_digest code_mode_host_download_url code_mode_host_digest + local codex_archive bwrap_archive code_mode_host_archive tmp candidate bwrap_candidate code_mode_host_candidate candidate_version backup missing_bwrap missing_code_mode_host bin="$(codex_bin)" if [[ -z "$bin" ]]; then echo "codex executable not found; set CODEX_BIN" >&2 @@ -585,8 +674,10 @@ check_updates() { codex_digest="${release_info[1]:-}" bwrap_download_url="${release_info[2]:-}" bwrap_digest="${release_info[3]:-}" - latest_version="${release_info[4]:-}" - latest_tag="${release_info[5]:-}" + code_mode_host_download_url="${release_info[4]:-}" + code_mode_host_digest="${release_info[5]:-}" + latest_version="${release_info[6]:-}" + latest_tag="${release_info[7]:-}" if [[ -z "$latest_version" || -z "$codex_download_url" ]]; then rm -rf "$tmp" echo "could not determine latest Codex release for $target" >&2 @@ -597,28 +688,59 @@ check_updates() { echo "could not determine latest bundled bwrap release for $target" >&2 return 1 fi + if [[ -z "$code_mode_host_download_url" ]]; then + rm -rf "$tmp" + echo "could not determine latest code-mode host release for $target" >&2 + return 1 + fi if ! version_gt "$latest_version" "$local_version"; then - if ! bwrap_required_for_target "$target" || bundled_bwrap_installed "$bin"; then + missing_bwrap=0 + missing_code_mode_host=0 + if bwrap_required_for_target "$target" && ! bundled_bwrap_installed "$bin"; then + missing_bwrap=1 + fi + if ! code_mode_host_installed "$bin"; then + missing_code_mode_host=1 + fi + if [[ "$missing_bwrap" == "0" && "$missing_code_mode_host" == "0" ]]; then rm -rf "$tmp" echo "Codex is already current: $local_version (latest $latest_version)" return 0 fi - echo "Codex is already current: $local_version (latest $latest_version); installing missing bundled bwrap" - bwrap_archive="$tmp/bwrap-$target.tar.gz" - curl -fL "$bwrap_download_url" -o "$bwrap_archive" - verify_digest "$bwrap_archive" "$bwrap_digest" - bwrap_candidate="$(extract_bwrap_binary "$bwrap_archive" "$tmp")" + echo "Codex is already current: $local_version (latest $latest_version); installing missing companion binaries" + bwrap_candidate="" + code_mode_host_candidate="" + if [[ "$missing_bwrap" == "1" ]]; then + bwrap_archive="$tmp/bwrap-$target.tar.gz" + curl -fL "$bwrap_download_url" -o "$bwrap_archive" + verify_digest "$bwrap_archive" "$bwrap_digest" + bwrap_candidate="$(extract_bwrap_binary "$bwrap_archive" "$tmp")" + fi + if [[ "$missing_code_mode_host" == "1" ]]; then + code_mode_host_archive="$tmp/codex-code-mode-host-$target.tar.gz" + curl -fL "$code_mode_host_download_url" -o "$code_mode_host_archive" + verify_digest "$code_mode_host_archive" "$code_mode_host_digest" + code_mode_host_candidate="$(extract_code_mode_host_binary "$code_mode_host_archive" "$tmp")" + fi backup="$bin.bak.$(date -u +%Y%m%d%H%M%S)" choose_install_prefix "$bin" - if install_bundled_bwrap "$bwrap_candidate" "$bin" "$backup"; then + if [[ -n "$bwrap_candidate" ]]; then + install_bundled_bwrap "$bwrap_candidate" "$bin" "$backup" run_install rm -f "$backup.bwrap.missing" - rm -rf "$tmp" - echo "Bundled bwrap installed: $(bundled_bwrap_path "$bin")" - return 0 + fi + if [[ -n "$code_mode_host_candidate" ]]; then + install_code_mode_host "$code_mode_host_candidate" "$bin" "$backup" + run_install rm -f "$backup.code-mode-host.missing" fi rm -rf "$tmp" - return 1 + if [[ "$missing_bwrap" == "1" ]]; then + echo "Bundled bwrap installed: $(bundled_bwrap_path "$bin")" + fi + if [[ "$missing_code_mode_host" == "1" ]]; then + echo "Code-mode host installed: $(code_mode_host_path "$bin")" + fi + return 0 fi echo "Codex update available: $local_version -> $latest_version ($latest_tag)" confirm_upgrade "$local_version" "$latest_version" "$bin" @@ -635,6 +757,10 @@ check_updates() { else bwrap_candidate="" fi + code_mode_host_archive="$tmp/codex-code-mode-host-$target.tar.gz" + curl -fL "$code_mode_host_download_url" -o "$code_mode_host_archive" + verify_digest "$code_mode_host_archive" "$code_mode_host_digest" + code_mode_host_candidate="$(extract_code_mode_host_binary "$code_mode_host_archive" "$tmp")" candidate_version="$(codex_version_from "$candidate")" if [[ "$candidate_version" != "$latest_version" ]]; then rm -rf "$tmp" @@ -646,11 +772,11 @@ check_updates() { choose_install_prefix "$bin" if is_running; then - handoff_upgrade "$candidate" "$bwrap_candidate" "$bin" "$backup" "$tmp" "$local_version" "$latest_version" + handoff_upgrade "$candidate" "$bwrap_candidate" "$code_mode_host_candidate" "$bin" "$backup" "$tmp" "$local_version" "$latest_version" return 0 fi - if apply_upgrade "$candidate" "$bwrap_candidate" "$bin" "$backup" "$local_version" "$latest_version"; then + if apply_upgrade "$candidate" "$bwrap_candidate" "$code_mode_host_candidate" "$bin" "$backup" "$local_version" "$latest_version"; then rm -rf "$tmp" return 0 fi @@ -659,7 +785,7 @@ check_updates() { } apply_upgrade_worker() { - local candidate="$1" bwrap_candidate="$2" bin="$3" backup="$4" update_dir="$5" local_version="$6" latest_version="$7" rc=0 + local candidate="$1" bwrap_candidate="$2" code_mode_host_candidate="$3" bin="$4" backup="$5" update_dir="$6" local_version="$7" latest_version="$8" rc=0 if [[ ! -x "$candidate" ]]; then echo "upgrade candidate is missing or not executable: $candidate" >&2 rm -rf "$update_dir" @@ -670,7 +796,12 @@ apply_upgrade_worker() { rm -rf "$update_dir" return 1 fi - if ! apply_upgrade "$candidate" "$bwrap_candidate" "$bin" "$backup" "$local_version" "$latest_version"; then + if [[ ! -x "$code_mode_host_candidate" ]]; then + echo "code-mode host candidate is missing or not executable: $code_mode_host_candidate" >&2 + rm -rf "$update_dir" + return 1 + fi + if ! apply_upgrade "$candidate" "$bwrap_candidate" "$code_mode_host_candidate" "$bin" "$backup" "$local_version" "$latest_version"; then rc=1 fi rm -rf "$update_dir" @@ -681,8 +812,16 @@ cmd="${1:-help}" case "$cmd" in start) shift || true - if [[ $# -ne 0 ]]; then usage; exit 2; fi - start_server + start_approve_for_me="$CODEX_APPROVE_FOR_ME" + while [[ $# -gt 0 ]]; do + case "$1" in + --approve-for-me) start_approve_for_me=1 ;; + -h|--help) usage; exit 0 ;; + *) echo "unknown start option: $1" >&2; usage; exit 2 ;; + esac + shift + done + start_server "$start_approve_for_me" ;; stop) shift || true @@ -700,7 +839,7 @@ case "$cmd" in ;; __apply-upgrade) shift || true - if [[ $# -ne 7 ]]; then echo "invalid upgrade worker arguments" >&2; exit 2; fi + if [[ $# -ne 8 ]]; then echo "invalid upgrade worker arguments" >&2; exit 2; fi apply_upgrade_worker "$@" ;; -h|--help|help)