From ca91f00ec7bd8772d746af690a46c02265caf68b Mon Sep 17 00:00:00 2001 From: yoshi Date: Sun, 5 Jul 2026 10:32:14 +0900 Subject: [PATCH] fix(codex): handle native Windows PIDs in bridge liveness checks --- scripts/delivery.sh | 8 +-- scripts/drivers/types/codex/_delivery.sh | 10 +++- scripts/drivers/types/codex/_session-start.sh | 2 +- .../types/codex/codex-bridge-launcher.sh | 6 +- scripts/drivers/types/codex/codex-monitor.sh | 4 +- scripts/lib/compat.sh | 59 +++++++++++++++++++ tests/test_delivery.bats | 23 ++++++++ 7 files changed, 100 insertions(+), 12 deletions(-) diff --git a/scripts/delivery.sh b/scripts/delivery.sh index 9ae0e94d..6a3ae527 100755 --- a/scripts/delivery.sh +++ b/scripts/delivery.sh @@ -335,8 +335,8 @@ stop_codex_bridge() { pidfile="$RUN_DIR/codex-bridge.$team.$name.pid" [ -f "$pidfile" ] || continue bpid=$(cat "$pidfile" 2>/dev/null || true) - if [ -n "$bpid" ] && kill -0 "$bpid" 2>/dev/null; then - kill "$bpid" 2>/dev/null && killed=$((killed + 1)) + if [ -n "$bpid" ] && compat_pid_alive "$bpid"; then + compat_kill_pid "$bpid" && killed=$((killed + 1)) fi # .appserver records which app-server URL the bridge was bound to (the # launcher's stale-binding guard); drop it with the rest so it cannot @@ -359,10 +359,10 @@ EOF server_pidfile="$RUN_DIR/codex-app-server.$project_hash.pid" if [ -f "$server_pidfile" ]; then server_pid="$(cat "$server_pidfile" 2>/dev/null || true)" - if [ -n "$server_pid" ] && kill -0 "$server_pid" 2>/dev/null; then + if [ -n "$server_pid" ] && compat_pid_alive "$server_pid"; then server_cmd="$(compat_get_cmdline "$server_pid" 2>/dev/null || true)" case "$server_cmd" in - *codex*app-server*) kill "$server_pid" 2>/dev/null || true ;; + *codex*app-server*) compat_kill_pid "$server_pid" || true ;; esac fi rm -f "$RUN_DIR/codex-app-server.$project_hash.pid" \ diff --git a/scripts/drivers/types/codex/_delivery.sh b/scripts/drivers/types/codex/_delivery.sh index cb1611f4..400e2419 100644 --- a/scripts/drivers/types/codex/_delivery.sh +++ b/scripts/drivers/types/codex/_delivery.sh @@ -64,7 +64,7 @@ agmsg_delivery_runtime_status() { fi found=1 - local base pidfile metafile pid meta_pid meta_project meta_type meta_ok + local base pidfile metafile pid meta_pid meta_project meta_type meta_ok meta_project_cmp project_cmp base="$RUN_DIR/codex-bridge.$team.$name" pidfile="$base.pid" metafile="$base.meta" @@ -90,14 +90,18 @@ agmsg_delivery_runtime_status() { meta_project=$(awk -F= '/^project=/{sub(/^project=/, ""); print; exit}' "$metafile" 2>/dev/null || true) meta_type=$(awk -F= '/^type=/{sub(/^type=/, ""); print; exit}' "$metafile" 2>/dev/null || true) [ -n "$meta_pid" ] && [ "$meta_pid" != "$pid" ] && meta_ok=0 - [ -n "$meta_project" ] && [ "$meta_project" != "$project" ] && meta_ok=0 + if [ -n "$meta_project" ]; then + meta_project_cmp=$(agmsg_canonical_path "$meta_project") + project_cmp=$(agmsg_canonical_path "$project") + [ "$meta_project_cmp" != "$project_cmp" ] && meta_ok=0 + fi [ -n "$meta_type" ] && [ "$meta_type" != "$type" ] && meta_ok=0 if [ "$meta_ok" -ne 1 ]; then echo "Codex bridge: $team/$name stale pidfile (metadata mismatch)" continue fi - if kill -0 "$pid" 2>/dev/null; then + if compat_pid_alive "$pid"; then echo "Codex bridge: $team/$name alive (pid $pid)" else echo "Codex bridge: $team/$name stale pidfile (pid $pid not running)" diff --git a/scripts/drivers/types/codex/_session-start.sh b/scripts/drivers/types/codex/_session-start.sh index 560fbd6a..a1410d6f 100644 --- a/scripts/drivers/types/codex/_session-start.sh +++ b/scripts/drivers/types/codex/_session-start.sh @@ -102,7 +102,7 @@ agmsg_session_start() { pidfile="$RUN_DIR/codex-bridge.$team.$name.pid" if [ -f "$pidfile" ]; then bridge_pid=$(cat "$pidfile" 2>/dev/null || true) - if [ -n "$bridge_pid" ] && kill -0 "$bridge_pid" 2>/dev/null; then + if [ -n "$bridge_pid" ] && compat_pid_alive "$bridge_pid"; then exit 0 fi fi diff --git a/scripts/drivers/types/codex/codex-bridge-launcher.sh b/scripts/drivers/types/codex/codex-bridge-launcher.sh index 6e6b31d2..07af7325 100755 --- a/scripts/drivers/types/codex/codex-bridge-launcher.sh +++ b/scripts/drivers/types/codex/codex-bridge-launcher.sh @@ -23,6 +23,8 @@ source "$SCRIPT_DIR/../../../lib/hash.sh" PROJECT_HASH="$(printf '%s' "$PROJECT" | agmsg_sha1)" REQUEST_FILE="$RUN_DIR/codex-bridge-request.$PROJECT_HASH" +# shellcheck source=../../../lib/compat.sh +source "$SCRIPT_DIR/../../../lib/compat.sh" # shellcheck source=../../../lib/node.sh source "$SCRIPT_DIR/../../../lib/node.sh" NODE_BIN="$(agmsg_resolve_node)" @@ -89,7 +91,7 @@ EOF if [ -f "$pidfile" ]; then bridge_pid="$(cat "$pidfile" 2>/dev/null || true)" - if [ -n "$bridge_pid" ] && kill -0 "$bridge_pid" 2>/dev/null; then + if [ -n "$bridge_pid" ] && compat_pid_alive "$bridge_pid"; then # Reuse only when the live bridge is bound to the CURRENT app-server. A # codex upgrade makes codex-monitor.sh kill the stale app-server and start a # fresh one on a new port (#237); a bridge still bound to the old URL stays @@ -101,7 +103,7 @@ EOF sleep 0.3 continue fi - kill "$bridge_pid" 2>/dev/null || true + compat_kill_pid "$bridge_pid" || true rm -f "$pidfile" "$appserver_file" fi fi diff --git a/scripts/drivers/types/codex/codex-monitor.sh b/scripts/drivers/types/codex/codex-monitor.sh index e0c4e5ee..7fc7a23c 100755 --- a/scripts/drivers/types/codex/codex-monitor.sh +++ b/scripts/drivers/types/codex/codex-monitor.sh @@ -122,7 +122,7 @@ if [ -f "$PORT_FILE" ] && [ -f "$SERVER_PID" ]; then # so a foreign process that grabbed the same port after ours died is not # mistaken for the bridge app-server. if [ -n "$existing_port" ] && [ -n "$existing_pid" ] \ - && kill -0 "$existing_pid" 2>/dev/null && port_alive "$existing_port"; then + && compat_pid_alive "$existing_pid" && port_alive "$existing_port"; then # Confirm the recorded pid is actually OUR codex app-server before trusting OR # killing it: a recycled pid could belong to an unrelated process while the # recorded port happens to answer via something else. Only reuse/kill when the @@ -139,7 +139,7 @@ if [ -f "$PORT_FILE" ] && [ -f "$SERVER_PID" ]; then if [ -z "$CODEX_VERSION" ] || [ "$existing_version" = "$CODEX_VERSION" ]; then PORT="$existing_port" else - kill "$existing_pid" 2>/dev/null || true + compat_kill_pid "$existing_pid" || true rm -f "$PORT_FILE" "$SERVER_PID" "$VERSION_FILE" fi ;; diff --git a/scripts/lib/compat.sh b/scripts/lib/compat.sh index 704f338d..df15fcc7 100644 --- a/scripts/lib/compat.sh +++ b/scripts/lib/compat.sh @@ -57,6 +57,65 @@ _compat_cim_cmdline() { | tr -d '\r' | tr '\\' '/' } +_compat_powershell_bin() { + if command -v powershell.exe >/dev/null 2>&1; then + printf '%s\n' powershell.exe + elif command -v pwsh.exe >/dev/null 2>&1; then + printf '%s\n' pwsh.exe + elif command -v pwsh >/dev/null 2>&1; then + printf '%s\n' pwsh + else + return 1 + fi +} + +compat_pid_alive() { + local pid="$1" psbin + case "$pid" in ''|*[!0-9]*) return 1 ;; esac + kill -0 "$pid" 2>/dev/null && return 0 + + _agmsg_detect_platform + case "$_agmsg_platform" in + msys) + local psw + psw=$(ps -W 2>/dev/null || true) + if [ -n "$psw" ]; then + printf '%s\n' "$psw" | awk -v pid="$pid" 'NR > 1 && $4 == pid { found=1 } END { exit found ? 0 : 1 }' && return 0 + return 1 + fi + psbin=$(_compat_powershell_bin) || return 1 + "$psbin" -NoProfile -Command \ + "\$p = Get-Process -Id $pid -ErrorAction SilentlyContinue; if (\$null -eq \$p) { exit 1 } else { exit 0 }" \ + >/dev/null 2>&1 + ;; + *) return 1 ;; + esac +} + +compat_kill_pid() { + local pid="$1" psbin + case "$pid" in ''|*[!0-9]*) return 1 ;; esac + kill "$pid" 2>/dev/null && return 0 + + _agmsg_detect_platform + case "$_agmsg_platform" in + msys) + local msys_pid psw + psw=$(ps -W 2>/dev/null || true) + if [ -n "$psw" ]; then + msys_pid=$(printf '%s\n' "$psw" | awk -v pid="$pid" 'NR > 1 && $4 == pid { print $1; exit }') + [ -n "$msys_pid" ] && kill "$msys_pid" 2>/dev/null && return 0 + return 1 + fi + psbin=$(_compat_powershell_bin) || return 1 + "$psbin" -NoProfile -Command \ + "Stop-Process -Id $pid -ErrorAction Stop" \ + >/dev/null 2>&1 + ;; + *) return 1 ;; + esac +} + # Get full command line of a process. Replaces: ps -o args= -p compat_get_cmdline() { local pid="$1" diff --git a/tests/test_delivery.bats b/tests/test_delivery.bats index a307e2a5..ac3cf22a 100644 --- a/tests/test_delivery.bats +++ b/tests/test_delivery.bats @@ -1492,6 +1492,29 @@ EOF trap - EXIT } +@test "delivery status (codex): canonical project metadata match is accepted" { + bash "$SCRIPTS/join.sh" team alice codex "$TEST_PROJECT" >/dev/null + bash "$SCRIPTS/delivery.sh" set monitor codex "$TEST_PROJECT" >/dev/null + mkdir -p "$TEST_SKILL_DIR/run" + + local dead_pid=999999 + printf '%s\n' "$dead_pid" > "$TEST_SKILL_DIR/run/codex-bridge.team.alice.pid" + local alias_project="$TEST_PROJECT/../$(basename "$TEST_PROJECT")" + cat > "$TEST_SKILL_DIR/run/codex-bridge.team.alice.meta" </dev/null