W
File size: 4,627 Bytes
2b64d42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env bash
# Install / update the Windsurf language server binary.
#
# Usage:
#   ./install-ls.sh                        # auto: our release β†’ Exafunction fallback
#   ./install-ls.sh /path/to/local.bin     # install a local file
#   ./install-ls.sh --file /path/to.bin    # same as above
#   ./install-ls.sh --url <direct-url>     # install from a custom URL
#
# Auto-detects platform (Linux / macOS) and architecture (x64 / arm64).
# Override install path with LS_INSTALL_PATH env var.
set -euo pipefail

OUR_RELEASE='https://github.com/dwgx/WindsurfAPI/releases/latest/download'
EXAFUNCTION_API='https://api.github.com/repos/Exafunction/codeium/releases/latest'
# v2.0.93: windsurf-linux-server-release extracts LS from official Windsurf desktop builds.
WINDSURF_LS_RELEASE='https://github.com/CaiJingLong/windsurf-linux-server-release/releases/latest/download'

log() { echo -e "\033[1;34m==>\033[0m $*"; }
err() { echo -e "\033[1;31m!!\033[0m  $*" >&2; }

# ─── Platform detection ────────────────────────────────
os="$(uname -s)"
arch="$(uname -m)"

case "$os" in
  Linux)
    case "$arch" in
      x86_64|amd64)  ASSET='language_server_linux_x64' ;;
      aarch64|arm64) ASSET='language_server_linux_arm' ;;
      *) err "Unsupported Linux arch: $arch"; exit 1 ;;
    esac
    DEFAULT_PATH="/opt/windsurf/${ASSET}"
    ;;
  Darwin)
    case "$arch" in
      x86_64)        ASSET='language_server_macos_x64' ;;
      arm64)         ASSET='language_server_macos_arm' ;;
      *) err "Unsupported macOS arch: $arch"; exit 1 ;;
    esac
    DEFAULT_PATH="$HOME/.windsurf/${ASSET}"
    ;;
  *)
    err "Unsupported OS: $os (only Linux and macOS are supported)"
    exit 1
    ;;
esac

TARGET="${LS_INSTALL_PATH:-$DEFAULT_PATH}"
log "Platform: $os $arch β†’ asset=$ASSET"
log "Target:   $TARGET"

mkdir -p "$(dirname "$TARGET")"

# Write to a sibling tmp file then atomic-rename onto the target. If the
# target is currently being executed (LS process has it mmap'd as the
# program text), Linux refuses an in-place open(O_WRONLY|O_TRUNC) with
# ETXTBSY ("file busy"). A rename(2), by contrast, just swaps the dirent
# pointer to a new inode β€” running processes keep their old inode and
# we get a fresh binary in place for the next exec.
TMP_TARGET="${TARGET}.new.$$"
trap 'rm -f "$TMP_TARGET"' EXIT

if [[ $# -gt 0 && "$1" == "--file" && -n "${2:-}" ]]; then
  log "Installing from local file: $2"
  cp -f "$2" "$TMP_TARGET"
elif [[ $# -gt 0 && "$1" != "--url" && "$1" != "--file" && -f "$1" ]]; then
  log "Installing from local file: $1"
  cp -f "$1" "$TMP_TARGET"
elif [[ $# -ge 2 && "$1" == "--url" ]]; then
  url="$2"
  log "Downloading from: $url"
  curl -fL --progress-bar -o "$TMP_TARGET" "$url"
else
  # Try our own GitHub release first (newer than Exafunction)
  our_url="${OUR_RELEASE}/${ASSET}"
  log "Trying WindsurfAPI release: $our_url"
  if curl -fL --progress-bar -o "$TMP_TARGET" "$our_url" 2>/dev/null; then
    log "Downloaded from WindsurfAPI release"
  else
    log "Not found in our release, trying windsurf-linux-server-release..."
    ws_url="${WINDSURF_LS_RELEASE}/${ASSET}"
    if curl -fL --progress-bar -o "$TMP_TARGET" "$ws_url" 2>/dev/null; then
      log "Downloaded from windsurf-linux-server-release (fresh WindSurf build)"
    else
      log "Not found there either, falling back to Exafunction..."
      if command -v jq >/dev/null 2>&1; then
        url="$(curl -fsSL "$EXAFUNCTION_API" | jq -r \
          --arg asset "$ASSET" '.assets[] | select(.name == $asset) | .browser_download_url')"
      else
        url="$(curl -fsSL "$EXAFUNCTION_API" | \
          grep -oE "https://[^\"]+/${ASSET}" | head -1)"
      fi
      if [[ -z "$url" ]]; then
        err "Could not find asset '$ASSET' in any release."
        err "Download manually from Windsurf desktop app:"
        err "  macOS: ~/Library/Application Support/Windsurf/.../bin/$ASSET"
        err "  Linux: ~/.windsurf/bin/$ASSET"
        exit 1
      fi
      log "Downloading: $url"
      curl -fL --progress-bar -o "$TMP_TARGET" "$url"
    fi
  fi
fi

chmod +x "$TMP_TARGET"
mv -f "$TMP_TARGET" "$TARGET"
trap - EXIT
size="$(du -h "$TARGET" | cut -f1)"
if command -v sha256sum >/dev/null 2>&1; then
  sha="$(sha256sum "$TARGET" | cut -c1-16)"
elif command -v shasum >/dev/null 2>&1; then
  sha="$(shasum -a 256 "$TARGET" | cut -c1-16)"
else
  sha="(no sha256 tool)"
fi
log "Installed: $TARGET ($size, sha256:$sha...)"

if [[ "$os" == "Darwin" ]]; then
  log ""
  log "macOS users: set this in your .env:"
  log "  LS_BINARY_PATH=$TARGET"
fi