fix(updater): SEA-aware install detection — recognise bin/claude.exe

After upstream switched to SEA (Single Executable Application) layout
in v2.1.114+, there is no cli.js anymore — only bin/claude.exe.

Before this fix:
- find_cli_js() searched only for cli.js → returned None on SEA installs
- get_installed_version() returned (None, None) → "Claude Code: not installed"
- is_patched() returned (False, [3 markers]) → false-negative even after
  successful patch
- cmd_check showed "not installed" right after a successful update
- cmd_update would loop reinstalling because patch markers seemed missing

This contributed to a user-facing incident where /model picker showed
only built-in models even though CLAUDE_CUSTOM_MODELS was set in
settings.json: the binary update path was triggering uninstall+reinstall
cycles instead of being recognised as already-patched.

Changes:
- New find_claude_artifact() finds either cli.js or bin/claude.exe,
  including the deeply-nested layout npm uses for SEA wrapper packages
- find_all_cli_js() returns both legacy and SEA artifacts
- is_patched() auto-detects layout: text scan for cli.js, bytes scan
  for claude.exe (markers: /*ae1_models_filter_patched*/,
  /*bypass_permissions_prompt*/)
- get_installed_version() reads package.json next to bin/ for SEA
- uclaude_install.sh adds binary marker verification at end of install
  so users immediately see if the SEA payload was patched

Tests: new tests/test_sea_detect.py covers all 4 detector functions
against fake SEA install layouts (including nested form). All 20 tests
pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
delta-cloud-208e
2026-04-26 07:45:35 +00:00
parent dd11b9784d
commit 0c2b9f056a
3 changed files with 367 additions and 52 deletions

View File

@@ -193,6 +193,40 @@ else
echo " (no $SETTINGS to verify — patcher may not have run)"
fi
# Verify the installed binary itself carries patcher markers — catches the
# case where patcher.config.json + settings.json are correct but the binary
# wasn't actually patched (e.g. npm overwrote it after install).
echo ""
echo " Binary patch verification:"
CLAUDE_BIN="$(readlink -f "$(command -v claude 2>/dev/null)" 2>/dev/null || true)"
if [ -n "$CLAUDE_BIN" ] && [ -f "$CLAUDE_BIN" ]; then
case "$(basename "$CLAUDE_BIN")" in
cli.js)
if grep -q '__CLAUDE_SETTINGS__\|/\*bypass_permissions_prompt\*/' "$CLAUDE_BIN" 2>/dev/null; then
echo " cli.js: OK (patcher markers present)"
else
echo " cli.js: ⚠ NOT patched ($CLAUDE_BIN — re-run uclaude_updater.py --force)"
fi
;;
claude.exe|claude)
# SEA binary — grep -a treats binary as text
sea_ok=true
grep -aq '/\*ae1_models_filter_patched' "$CLAUDE_BIN" 2>/dev/null || sea_ok=false
grep -aq '/\*bypass_permissions_prompt' "$CLAUDE_BIN" 2>/dev/null || sea_ok=false
if $sea_ok; then
echo " SEA binary: OK (ae1_models_filter + bypass_permissions markers)"
else
echo " SEA binary: ⚠ NOT fully patched ($CLAUDE_BIN — re-run uclaude_updater.py --force)"
fi
;;
*)
echo " (unknown artifact: $CLAUDE_BIN)"
;;
esac
else
echo " (no claude binary on PATH — check installation)"
fi
echo ""
echo " Run claude: claude # interactive"
echo " Update later: cd $INSTALL_DIR && sudo bash claude/uclaude_update.sh"