Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
83 lines
2.1 KiB
Go
83 lines
2.1 KiB
Go
package daemon
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/tutti-os/tutti/apps/cli/internal/defaults"
|
|
)
|
|
|
|
type Endpoint struct {
|
|
Addr string
|
|
Token string
|
|
}
|
|
|
|
type endpointFile struct {
|
|
Version int `json:"version"`
|
|
Addr string `json:"addr"`
|
|
Auth endpointFileAuth `json:"auth"`
|
|
}
|
|
|
|
type endpointFileAuth struct {
|
|
Scheme string `json:"scheme"`
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
func DiscoverEndpoint() (Endpoint, error) {
|
|
return ReadEndpointFile(ListenerInfoPath())
|
|
}
|
|
|
|
func ReadEndpointFile(path string) (Endpoint, error) {
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return Endpoint{}, fmt.Errorf("daemon endpoint is not available; start the Tutti desktop app")
|
|
}
|
|
return Endpoint{}, fmt.Errorf("read daemon endpoint: %w", err)
|
|
}
|
|
|
|
var payload endpointFile
|
|
if err := json.Unmarshal(content, &payload); err != nil {
|
|
return Endpoint{}, fmt.Errorf("daemon endpoint file is invalid JSON")
|
|
}
|
|
|
|
addr := strings.TrimSpace(payload.Addr)
|
|
if addr == "" {
|
|
return Endpoint{}, fmt.Errorf("daemon endpoint file does not contain addr")
|
|
}
|
|
if payload.Version != 0 && payload.Version != 1 {
|
|
return Endpoint{}, fmt.Errorf("daemon endpoint file version is not supported")
|
|
}
|
|
|
|
scheme := strings.ToLower(strings.TrimSpace(payload.Auth.Scheme))
|
|
token := strings.TrimSpace(payload.Auth.Token)
|
|
if scheme != "bearer" || token == "" {
|
|
return Endpoint{}, fmt.Errorf("daemon endpoint file does not contain bearer auth")
|
|
}
|
|
|
|
return Endpoint{Addr: addr, Token: token}, nil
|
|
}
|
|
|
|
func (endpoint Endpoint) BaseURL() (string, error) {
|
|
addr := strings.TrimSpace(endpoint.Addr)
|
|
if addr == "" {
|
|
return "", fmt.Errorf("daemon address is empty")
|
|
}
|
|
if strings.HasPrefix(addr, "http://") || strings.HasPrefix(addr, "https://") {
|
|
parsed, err := url.Parse(addr)
|
|
if err != nil || parsed.Host == "" {
|
|
return "", fmt.Errorf("daemon address is invalid")
|
|
}
|
|
return strings.TrimRight(addr, "/"), nil
|
|
}
|
|
return "http://" + strings.TrimRight(addr, "/"), nil
|
|
}
|
|
|
|
func ListenerInfoPath() string {
|
|
return defaults.ResolveDefaultsFromEnv().State.TuttidListenerInfoPath
|
|
}
|