@viudes/windsurf-api 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +148 -0
- package/LICENSE +21 -0
- package/README.md +135 -0
- package/bin/windsurf-api.js +5 -0
- package/config.example.json +21 -0
- package/dist/app/cli.js +334 -0
- package/dist/app/index.js +286 -0
- package/dist/dashboard/data/contributors.json +337 -0
- package/dist/dashboard/i18n/en.json +1106 -0
- package/dist/dashboard/i18n/zh-CN.json +1106 -0
- package/dist/dashboard/index-sketch.html +3564 -0
- package/dist/dashboard/index.html +6327 -0
- package/install-ls.sh +174 -0
- package/package.json +69 -0
package/install-ls.sh
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install / update the Windsurf language server binary.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# ./install-ls.sh # auto: WindsurfAPI release → maintained LS mirror → Exafunction fallback
|
|
6
|
+
# ./install-ls.sh /path/to/local.bin # install a local file
|
|
7
|
+
# ./install-ls.sh --file /path/to.bin # same as above
|
|
8
|
+
# ./install-ls.sh --url <direct-url> # install from a custom URL
|
|
9
|
+
#
|
|
10
|
+
# Auto-detects platform (Linux / macOS) and architecture (x64 / arm64).
|
|
11
|
+
# Override install path with LS_INSTALL_PATH env var.
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
OUR_RELEASE='https://github.com/dwgx/WindsurfAPI/releases/latest/download'
|
|
15
|
+
EXAFUNCTION_API='https://api.github.com/repos/Exafunction/codeium/releases/latest'
|
|
16
|
+
# Maintained mirror for LS binaries extracted from official Windsurf/Devin Desktop builds.
|
|
17
|
+
WINDSURF_LS_RELEASE="${WINDSURFAPI_LS_RELEASE:-https://github.com/dwgx/windsurf-ls-release/releases/latest/download}"
|
|
18
|
+
|
|
19
|
+
log() { echo -e "\033[1;34m==>\033[0m $*"; }
|
|
20
|
+
err() { echo -e "\033[1;31m!!\033[0m $*" >&2; }
|
|
21
|
+
|
|
22
|
+
sha256_file() {
|
|
23
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
24
|
+
sha256sum "$1" | awk '{print $1}'
|
|
25
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
26
|
+
shasum -a 256 "$1" | awk '{print $1}'
|
|
27
|
+
else
|
|
28
|
+
return 127
|
|
29
|
+
fi
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
verify_release_asset_checksum() {
|
|
33
|
+
local release_base="$1"
|
|
34
|
+
local asset="$2"
|
|
35
|
+
local file="$3"
|
|
36
|
+
local checksums_file="${TMP_CHECKSUMS:-${file}.SHA256SUMS}"
|
|
37
|
+
local checksums_url="${release_base}/SHA256SUMS"
|
|
38
|
+
|
|
39
|
+
log "Trying checksum file: $checksums_url"
|
|
40
|
+
if ! curl -fsL -o "$checksums_file" "$checksums_url"; then
|
|
41
|
+
rm -f "$checksums_file"
|
|
42
|
+
log "SHA256SUMS not available; skipping mirror checksum verification"
|
|
43
|
+
return 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
local expected
|
|
47
|
+
expected="$(awk -v asset="$asset" '{ checksum_asset = $2; sub(/\015$/, "", checksum_asset); if (checksum_asset == asset && length($1) == 64 && $1 ~ /^[0-9a-fA-F]+$/) { print tolower($1); exit } }' "$checksums_file")"
|
|
48
|
+
rm -f "$checksums_file"
|
|
49
|
+
if [[ -z "$expected" ]]; then
|
|
50
|
+
err "SHA256SUMS from $release_base does not list $asset"
|
|
51
|
+
return 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
local actual
|
|
55
|
+
if ! actual="$(sha256_file "$file")"; then
|
|
56
|
+
log "No sha256 tool available; skipping mirror checksum verification"
|
|
57
|
+
return 0
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
if [[ "$actual" != "$expected" ]]; then
|
|
61
|
+
err "Checksum mismatch for $asset"
|
|
62
|
+
err "Expected: $expected"
|
|
63
|
+
err "Actual: $actual"
|
|
64
|
+
return 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
log "Verified $asset against SHA256SUMS"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# ─── Platform detection ────────────────────────────────
|
|
71
|
+
os="$(uname -s)"
|
|
72
|
+
arch="$(uname -m)"
|
|
73
|
+
|
|
74
|
+
case "$os" in
|
|
75
|
+
Linux)
|
|
76
|
+
case "$arch" in
|
|
77
|
+
x86_64|amd64) ASSET='language_server_linux_x64' ;;
|
|
78
|
+
aarch64|arm64) ASSET='language_server_linux_arm' ;;
|
|
79
|
+
*) err "Unsupported Linux arch: $arch"; exit 1 ;;
|
|
80
|
+
esac
|
|
81
|
+
DEFAULT_PATH="/opt/windsurf/${ASSET}"
|
|
82
|
+
;;
|
|
83
|
+
Darwin)
|
|
84
|
+
case "$arch" in
|
|
85
|
+
x86_64) ASSET='language_server_macos_x64' ;;
|
|
86
|
+
arm64) ASSET='language_server_macos_arm' ;;
|
|
87
|
+
*) err "Unsupported macOS arch: $arch"; exit 1 ;;
|
|
88
|
+
esac
|
|
89
|
+
DEFAULT_PATH="$HOME/.windsurf/${ASSET}"
|
|
90
|
+
;;
|
|
91
|
+
*)
|
|
92
|
+
err "Unsupported OS: $os (only Linux and macOS are supported)"
|
|
93
|
+
exit 1
|
|
94
|
+
;;
|
|
95
|
+
esac
|
|
96
|
+
|
|
97
|
+
TARGET="${LS_INSTALL_PATH:-$DEFAULT_PATH}"
|
|
98
|
+
log "Platform: $os $arch → asset=$ASSET"
|
|
99
|
+
log "Target: $TARGET"
|
|
100
|
+
|
|
101
|
+
mkdir -p "$(dirname "$TARGET")"
|
|
102
|
+
|
|
103
|
+
# Write to a sibling tmp file then atomic-rename onto the target. If the
|
|
104
|
+
# target is currently being executed (LS process has it mmap'd as the
|
|
105
|
+
# program text), Linux refuses an in-place open(O_WRONLY|O_TRUNC) with
|
|
106
|
+
# ETXTBSY ("file busy"). A rename(2), by contrast, just swaps the dirent
|
|
107
|
+
# pointer to a new inode — running processes keep their old inode and
|
|
108
|
+
# we get a fresh binary in place for the next exec.
|
|
109
|
+
TMP_TARGET="${TARGET}.new.$$"
|
|
110
|
+
TMP_CHECKSUMS="${TMP_TARGET}.SHA256SUMS"
|
|
111
|
+
trap 'rm -f "$TMP_TARGET" "$TMP_CHECKSUMS"' EXIT
|
|
112
|
+
|
|
113
|
+
if [[ $# -gt 0 && "$1" == "--file" && -n "${2:-}" ]]; then
|
|
114
|
+
log "Installing from local file: $2"
|
|
115
|
+
cp -f "$2" "$TMP_TARGET"
|
|
116
|
+
elif [[ $# -gt 0 && "$1" != "--url" && "$1" != "--file" && -f "$1" ]]; then
|
|
117
|
+
log "Installing from local file: $1"
|
|
118
|
+
cp -f "$1" "$TMP_TARGET"
|
|
119
|
+
elif [[ $# -ge 2 && "$1" == "--url" ]]; then
|
|
120
|
+
url="$2"
|
|
121
|
+
log "Downloading from: $url"
|
|
122
|
+
curl -fL --progress-bar -o "$TMP_TARGET" "$url"
|
|
123
|
+
else
|
|
124
|
+
# Try our own GitHub release first, then the maintained public LS mirror,
|
|
125
|
+
# then the older Exafunction/codeium release as a last resort.
|
|
126
|
+
our_url="${OUR_RELEASE}/${ASSET}"
|
|
127
|
+
log "Trying WindsurfAPI release: $our_url"
|
|
128
|
+
if curl -fL --progress-bar -o "$TMP_TARGET" "$our_url" 2>/dev/null; then
|
|
129
|
+
log "Downloaded from WindsurfAPI release"
|
|
130
|
+
else
|
|
131
|
+
log "Not found in WindsurfAPI release, trying maintained Windsurf LS mirror..."
|
|
132
|
+
ws_url="${WINDSURF_LS_RELEASE}/${ASSET}"
|
|
133
|
+
log "Trying maintained Windsurf LS mirror: $ws_url"
|
|
134
|
+
if curl -fL --progress-bar -o "$TMP_TARGET" "$ws_url"; then
|
|
135
|
+
log "Downloaded from maintained Windsurf LS mirror"
|
|
136
|
+
verify_release_asset_checksum "$WINDSURF_LS_RELEASE" "$ASSET" "$TMP_TARGET"
|
|
137
|
+
else
|
|
138
|
+
log "Not found in maintained Windsurf LS mirror, falling back to Exafunction..."
|
|
139
|
+
if command -v jq >/dev/null 2>&1; then
|
|
140
|
+
url="$(curl -fsSL "$EXAFUNCTION_API" | jq -r \
|
|
141
|
+
--arg asset "$ASSET" '.assets[] | select(.name == $asset) | .browser_download_url')"
|
|
142
|
+
else
|
|
143
|
+
url="$(curl -fsSL "$EXAFUNCTION_API" | \
|
|
144
|
+
grep -oE "https://[^\"]+/${ASSET}" | head -1)"
|
|
145
|
+
fi
|
|
146
|
+
if [[ -z "$url" ]]; then
|
|
147
|
+
err "Could not find asset '$ASSET' in any release."
|
|
148
|
+
err "Download manually from Windsurf desktop app:"
|
|
149
|
+
err " macOS: ~/Library/Application Support/Windsurf/.../bin/$ASSET"
|
|
150
|
+
err " Linux: ~/.windsurf/bin/$ASSET"
|
|
151
|
+
exit 1
|
|
152
|
+
fi
|
|
153
|
+
log "Downloading: $url"
|
|
154
|
+
curl -fL --progress-bar -o "$TMP_TARGET" "$url"
|
|
155
|
+
fi
|
|
156
|
+
fi
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
chmod +x "$TMP_TARGET"
|
|
160
|
+
mv -f "$TMP_TARGET" "$TARGET"
|
|
161
|
+
trap - EXIT
|
|
162
|
+
size="$(du -h "$TARGET" | cut -f1)"
|
|
163
|
+
if full_sha="$(sha256_file "$TARGET")"; then
|
|
164
|
+
sha="$(printf '%s' "$full_sha" | cut -c1-16)"
|
|
165
|
+
else
|
|
166
|
+
sha="(no sha256 tool)"
|
|
167
|
+
fi
|
|
168
|
+
log "Installed: $TARGET ($size, sha256:$sha...)"
|
|
169
|
+
|
|
170
|
+
if [[ "$os" == "Darwin" ]]; then
|
|
171
|
+
log ""
|
|
172
|
+
log "macOS users: set this in your .env:"
|
|
173
|
+
log " LS_BINARY_PATH=$TARGET"
|
|
174
|
+
fi
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@viudes/windsurf-api",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "Windsurf to OpenAI + Anthropic compatible API proxy. Turns Windsurf's 100+ AI models (Claude, GPT, Gemini, DeepSeek, Grok, Qwen, Kimi, GLM, SWE) into dual-protocol API endpoints. Zero npm deps.",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "dist/app/index.js",
|
|
10
|
+
"bin": {
|
|
11
|
+
"windsurf-api": "bin/windsurf-api.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/app/",
|
|
15
|
+
"dist/dashboard/",
|
|
16
|
+
"bin/",
|
|
17
|
+
"install-ls.sh",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"README.md",
|
|
20
|
+
".env.example",
|
|
21
|
+
"config.example.json"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "node scripts/build-js.mjs",
|
|
25
|
+
"prepublishOnly": "npm run build",
|
|
26
|
+
"start": "node src/app/index.js",
|
|
27
|
+
"start:bun": "bun run src/app/index.js",
|
|
28
|
+
"dev": "node --watch src/app/index.js",
|
|
29
|
+
"build:exe": "bun run scripts/build-exe.mjs",
|
|
30
|
+
"build:exe:all": "bun run scripts/build-exe.mjs --all",
|
|
31
|
+
"test": "node --import ./test/setup-env.mjs --test --test-force-exit test/*/*.test.js",
|
|
32
|
+
"test:shard": "node scripts/run-test-shard.mjs",
|
|
33
|
+
"test:release": "node --import ./test/setup-env.mjs --test --test-force-exit test/conversation-cache/cache.test.js test/account-pool/dashboard-api.test.js test/_meta/dashboard-syntax.test.js test/native-bridge/native-tool-routing.test.js test/language-server/proto-trace.test.js test/_meta/secret-scan.test.js test/_meta/test-shard-script.test.js test/_meta/release-workflow.test.js test/_meta/changelog-script.test.js test/core/version.test.js",
|
|
34
|
+
"smoke:native-bridge": "node scripts/native-bridge-smoke.mjs",
|
|
35
|
+
"smoke:special-agent": "node scripts/special-agent-smoke.mjs",
|
|
36
|
+
"smoke:lsp-matrix": "node scripts/lsp-capacity-matrix.mjs",
|
|
37
|
+
"probe:web-search": "node scripts/web-search-direct-probe.mjs",
|
|
38
|
+
"sync:contributors": "node scripts/sync-docs-contributors.mjs",
|
|
39
|
+
"secret-scan": "node scripts/secret-scan.mjs"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=24.0.0",
|
|
43
|
+
"bun": ">=1.2.23"
|
|
44
|
+
},
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"author": "andersonviudes <andersonvieiraviudes@gmail.com> (https://github.com/andersonviudes)",
|
|
47
|
+
"homepage": "https://github.com/andersonviudes/windsurf-openai#readme",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/andersonviudes/windsurf-openai.git"
|
|
51
|
+
},
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/andersonviudes/windsurf-openai/issues"
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"windsurf",
|
|
57
|
+
"devin",
|
|
58
|
+
"codeium",
|
|
59
|
+
"openai",
|
|
60
|
+
"anthropic",
|
|
61
|
+
"claude",
|
|
62
|
+
"gpt",
|
|
63
|
+
"gemini",
|
|
64
|
+
"api-proxy",
|
|
65
|
+
"llm-gateway",
|
|
66
|
+
"reverse-engineering",
|
|
67
|
+
"zero-dependency"
|
|
68
|
+
]
|
|
69
|
+
}
|