Watch
1
0
Fork
You've already forked RedFlag
0
RedFlag/agent/internal/localapi/client.go
Fimeg f487de554a desktop: become the machine console
Qt/QML now carries 11 local views while the Agent owns observation and intent. Tauri, WebKit, and the second React desktop build leave together. Linux ships first; Windows waits for a native Qt runner.
2026-09-01 08:29:58 -04:00

86 lines
2.1 KiB
Go

package localapi
import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"time"
)
const localHTTPBaseURL = "http://redflag.local"
// ClientOptions configures local IPC client access. Defaults mirror the agent
// listener and remain platform-specific.
type ClientOptions struct {
UnixSocketPath string
WindowsPipeName string
Timeout time.Duration
}
// Snapshot is the compact local state needed by status probes and Desktop.
type Snapshot struct {
Identity IdentityResponse `json:"identity"`
Status StatusResponse `json:"status"`
}
// FetchSnapshot reads the local agent API over the platform IPC transport.
func FetchSnapshot(ctx context.Context, opts ClientOptions) (*Snapshot, error) {
httpClient := newLocalHTTPClient(opts)
identity, err := fetchJSON[IdentityResponse](ctx, httpClient, "/v1/identity")
if err != nil {
return nil, fmt.Errorf("fetch identity: %w", err)
}
status, err := fetchJSON[StatusResponse](ctx, httpClient, "/v1/status")
if err != nil {
return nil, fmt.Errorf("fetch status: %w", err)
}
return &Snapshot{
Identity: identity,
Status: status,
}, nil
}
func newLocalHTTPClient(opts ClientOptions) *http.Client {
timeout := opts.Timeout
if timeout == 0 {
timeout = 5 * time.Second
}
return &http.Client{
Timeout: timeout,
Transport: &http.Transport{
DialContext: dialLocalContext(opts),
DisableKeepAlives: true,
Proxy: nil,
},
}
}
func fetchJSON[T any](ctx context.Context, httpClient *http.Client, path string) (T, error) {
var value T
req, err := http.NewRequestWithContext(ctx, http.MethodGet, localHTTPBaseURL+path, nil)
if err != nil {
return value, err
}
resp, err := httpClient.Do(req)
if err != nil {
return value, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return value, fmt.Errorf("local API returned %s", resp.Status)
}
if err := json.NewDecoder(resp.Body).Decode(&value); err != nil {
return value, err
}
return value, nil
}
func dialLocalContext(opts ClientOptions) func(context.Context, string, string) (net.Conn, error) {
return platformDialLocalContext(opts)
}