180 lines
4.3 KiB
Go

package windsurf
import (
"context"
"fmt"
"sync"
)
type LSConnector interface {
Mode() string
Acquire(ctx context.Context, proxyURL string) (*LSLease, error)
Health(ctx context.Context) error
Status() *LSConnectorStatus
// Shutdown releases any resources owned by the connector. Connectors
// that only dial external endpoints implement this as a no-op; the
// embedded mode terminates its spawned LS processes here.
Shutdown()
}
type LSLease struct {
Mode string
Endpoint string
Client *LocalLSClient
Release func()
}
type LSConnectorStatus struct {
Mode string `json:"mode"`
Healthy bool `json:"healthy"`
Instances int `json:"instances"`
Endpoint string `json:"endpoint,omitempty"`
}
type DockerConnector struct {
host string
port int
csrfToken string
client *LocalLSClient
once sync.Once
}
func NewDockerConnector(host string, port int, csrfToken string) *DockerConnector {
return &DockerConnector{host: host, port: port, csrfToken: csrfToken}
}
func (d *DockerConnector) Mode() string { return "docker" }
func (d *DockerConnector) Acquire(_ context.Context, _ string) (*LSLease, error) {
d.once.Do(func() {
d.client = NewLocalLSClient(d.port, d.csrfToken)
d.client.BaseURL = fmt.Sprintf("http://%s:%d", d.host, d.port)
})
return &LSLease{
Mode: "docker",
Endpoint: fmt.Sprintf("%s:%d", d.host, d.port),
Client: d.client,
Release: func() {},
}, nil
}
func (d *DockerConnector) Health(ctx context.Context) error {
_, err := d.Acquire(ctx, "")
return err
}
func (d *DockerConnector) Status() *LSConnectorStatus {
return &LSConnectorStatus{
Mode: "docker",
Healthy: d.client != nil,
Instances: 1,
Endpoint: fmt.Sprintf("%s:%d", d.host, d.port),
}
}
// Shutdown is a no-op: DockerConnector only dials a remote endpoint and
// owns no long-lived goroutines or child processes.
func (d *DockerConnector) Shutdown() {}
type EmbeddedConnector struct {
pool *LSPool
}
func NewEmbeddedConnector(pool *LSPool) *EmbeddedConnector {
return &EmbeddedConnector{pool: pool}
}
func (e *EmbeddedConnector) Mode() string { return "embedded" }
func (e *EmbeddedConnector) Acquire(ctx context.Context, proxyURL string) (*LSLease, error) {
entry, err := e.pool.Ensure(ctx, proxyURL)
if err != nil {
return nil, err
}
return &LSLease{
Mode: "embedded",
Endpoint: fmt.Sprintf("localhost:%d", entry.Port),
Client: entry.Client,
Release: func() {},
}, nil
}
func (e *EmbeddedConnector) Health(_ context.Context) error {
status := e.pool.Status()
if !status.Running {
return fmt.Errorf("no LS instances running")
}
return nil
}
func (e *EmbeddedConnector) Status() *LSConnectorStatus {
status := e.pool.Status()
readyCount := 0
for _, inst := range status.Instances {
if inst.Ready {
readyCount++
}
}
return &LSConnectorStatus{
Mode: "embedded",
Healthy: readyCount > 0,
Instances: len(status.Instances),
}
}
// Shutdown terminates every LS process in the pool. Must be called on
// application teardown; otherwise child processes leak until the OS
// reaps them at exit.
func (e *EmbeddedConnector) Shutdown() {
if e == nil || e.pool == nil {
return
}
e.pool.Shutdown()
}
type ExternalConnector struct {
baseURL string
port int
csrfToken string
client *LocalLSClient
once sync.Once
}
func NewExternalConnector(baseURL string, port int, csrfToken string) *ExternalConnector {
return &ExternalConnector{baseURL: baseURL, port: port, csrfToken: csrfToken}
}
func (x *ExternalConnector) Mode() string { return "external" }
func (x *ExternalConnector) Acquire(_ context.Context, _ string) (*LSLease, error) {
x.once.Do(func() {
x.client = NewLocalLSClient(x.port, x.csrfToken)
if x.baseURL != "" {
x.client.BaseURL = x.baseURL
}
})
return &LSLease{
Mode: "external",
Endpoint: x.baseURL,
Client: x.client,
Release: func() {},
}, nil
}
func (x *ExternalConnector) Health(ctx context.Context) error {
_, err := x.Acquire(ctx, "")
return err
}
func (x *ExternalConnector) Status() *LSConnectorStatus {
return &LSConnectorStatus{
Mode: "external",
Healthy: x.client != nil,
Instances: 1,
Endpoint: x.baseURL,
}
}
// Shutdown is a no-op: ExternalConnector only dials a remote endpoint.
func (x *ExternalConnector) Shutdown() {}