118 lines
3.6 KiB
Go
118 lines
3.6 KiB
Go
// Package windsurf is a minimal Go client for the Windsurf LanguageServerService (local gRPC)
|
|
// and upstream Connect-RPC JSON endpoints.
|
|
//
|
|
// Portions of this file derive from https://github.com/seven7763/windsurf-tools (MIT, 2025 shaoyu521).
|
|
// See ./LICENSE for full attribution.
|
|
package windsurf
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"os"
|
|
)
|
|
|
|
// ── Constants ──────────────────────────────────────────────────────────────
|
|
|
|
const (
|
|
DefaultBaseURL = "https://server.self-serve.windsurf.com"
|
|
|
|
AppName = "windsurf"
|
|
AppVersion = "1.48.2"
|
|
ExtensionVersion = "1.9600.41"
|
|
IDEVersion = ExtensionVersion
|
|
ClientVersion = "2.0.63"
|
|
UserAgent = "connect-go/1.18.1 (go1.26.1)"
|
|
|
|
// DefaultRuntimeOS / DefaultHardwareArch are what we advertise in
|
|
// protocol metadata when the WINDSURF_METADATA_OS /
|
|
// WINDSURF_METADATA_ARCH env vars are unset. They match the Linux LS
|
|
// binary we spawn by default, so the upstream never sees a mismatch
|
|
// between advertised OS and the actual LS behavior.
|
|
DefaultRuntimeOS = "linux"
|
|
DefaultHardwareArch = "x86_64"
|
|
)
|
|
|
|
// RuntimeOS returns the OS string sent in protocol metadata, consulting
|
|
// WINDSURF_METADATA_OS on each call so tests can flip it with t.Setenv.
|
|
func RuntimeOS() string {
|
|
if v := os.Getenv("WINDSURF_METADATA_OS"); v != "" {
|
|
return v
|
|
}
|
|
return DefaultRuntimeOS
|
|
}
|
|
|
|
// HardwareArch returns the hardware string sent in protocol metadata,
|
|
// consulting WINDSURF_METADATA_ARCH on each call.
|
|
func HardwareArch() string {
|
|
if v := os.Getenv("WINDSURF_METADATA_ARCH"); v != "" {
|
|
return v
|
|
}
|
|
return DefaultHardwareArch
|
|
}
|
|
|
|
// ── Protobuf wire encoding ─────────────────────────────────────────────────
|
|
|
|
func writeVarint(value uint64) []byte {
|
|
var parts []byte
|
|
for value > 0x7F {
|
|
parts = append(parts, byte(value&0x7F)|0x80)
|
|
value >>= 7
|
|
}
|
|
parts = append(parts, byte(value))
|
|
return parts
|
|
}
|
|
|
|
func encodeBytesField(fieldNum uint64, data []byte) []byte {
|
|
tag := writeVarint((fieldNum << 3) | 2)
|
|
length := writeVarint(uint64(len(data)))
|
|
out := make([]byte, 0, len(tag)+len(length)+len(data))
|
|
out = append(out, tag...)
|
|
out = append(out, length...)
|
|
out = append(out, data...)
|
|
return out
|
|
}
|
|
|
|
func encodeStringField(fieldNum uint64, s string) []byte {
|
|
return encodeBytesField(fieldNum, []byte(s))
|
|
}
|
|
|
|
func encodeVarintField(fieldNum uint64, value uint64) []byte {
|
|
tag := writeVarint((fieldNum << 3) | 0)
|
|
val := writeVarint(value)
|
|
out := make([]byte, 0, len(tag)+len(val))
|
|
out = append(out, tag...)
|
|
out = append(out, val...)
|
|
return out
|
|
}
|
|
|
|
// ReadVarint reads a varint from data starting at pos.
|
|
func ReadVarint(data []byte, pos int) (val uint64, newPos int, ok bool) {
|
|
var shift uint
|
|
for pos < len(data) {
|
|
b := data[pos]
|
|
pos++
|
|
val |= uint64(b&0x7F) << shift
|
|
shift += 7
|
|
if (b & 0x80) == 0 {
|
|
return val, pos, true
|
|
}
|
|
if shift >= 64 {
|
|
return 0, pos, false
|
|
}
|
|
}
|
|
return 0, pos, false
|
|
}
|
|
|
|
// ── UUID ───────────────────────────────────────────────────────────────────
|
|
|
|
func generateUUID() string {
|
|
var buf [16]byte
|
|
_, _ = readRandom(buf[:])
|
|
buf[6] = (buf[6] & 0x0f) | 0x40
|
|
buf[8] = (buf[8] & 0x3f) | 0x80
|
|
return hex.EncodeToString(buf[0:4]) + "-" +
|
|
hex.EncodeToString(buf[4:6]) + "-" +
|
|
hex.EncodeToString(buf[6:8]) + "-" +
|
|
hex.EncodeToString(buf[8:10]) + "-" +
|
|
hex.EncodeToString(buf[10:16])
|
|
}
|