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])
}