Reconnect the app's command channel after sleep; list clients from it

This commit is contained in:
Ian Gulliver
2026-10-18 09:16:31 -07:00
parent 931d1b9a96
commit 1363a3604d
11 changed files with 444 additions and 388 deletions
+15 -28
View File
@@ -35,7 +35,7 @@ func (m *Manager) clientTools(l *live) []mcp.Tool {
{
Name: "list_clients",
Description: "List the Macs with byre's app open: the human's own machines, not the one byre (and your worktree) runs on. " +
"Each has its name, which the other client tools take as client, the human using it, and when the human last did something in byre from it. " +
"Each has its name, which the other client tools take as client, the id of that running app (pass it as client instead when two apps are open on one Mac), the human using it, and when the human last did something in byre from it. " +
"The most recently active one is listed first; it is usually the Mac the human sits at.",
InputSchema: json.RawMessage(`{"type": "object", "properties": {}}`),
Handler: func(ctx context.Context, raw json.RawMessage) (string, error) {
@@ -50,7 +50,7 @@ func (m *Manager) clientTools(l *live) []mcp.Tool {
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"client": {"type": "string", "description": "The client's name from list_clients."},
"client": {"type": "string", "description": "The client's name from list_clients, or its id when several are connected from that Mac."},
"command": {"type": "string"},
"timeout_ms": {"type": "integer", "description": "Kill the command after this long (default 120000, at most 600000)."}
},
@@ -67,7 +67,7 @@ func (m *Manager) clientTools(l *live) []mcp.Tool {
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"client": {"type": "string", "description": "The client's name from list_clients."},
"client": {"type": "string", "description": "The client's name from list_clients, or its id when several are connected from that Mac."},
"path": {"type": "string", "description": "The file or directory, relative to your worktree."},
"dest": {"type": "string", "description": "Its new path on the client, absolute or starting with ~/, e.g. ~/Applications/byre.app."}
},
@@ -84,7 +84,7 @@ func (m *Manager) clientTools(l *live) []mcp.Tool {
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"client": {"type": "string", "description": "The client's name from list_clients."},
"client": {"type": "string", "description": "The client's name from list_clients, or its id when several are connected from that Mac."},
"path": {"type": "string", "description": "The file or directory on the client, absolute or starting with ~/."},
"dest": {"type": "string", "description": "Its new path, relative to your worktree; parent directories are created."}
},
@@ -101,7 +101,7 @@ func (m *Manager) clientTools(l *live) []mcp.Tool {
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"client": {"type": "string", "description": "The client's name from list_clients."},
"client": {"type": "string", "description": "The client's name from list_clients, or its id when several are connected from that Mac."},
"path": {"type": "string", "description": "The file, absolute or starting with ~/."},
"offset": {"type": "integer", "description": "The line number to start from (default 1)."},
"limit": {"type": "integer", "description": "The most lines to return (default 2000)."}
@@ -118,7 +118,7 @@ func (m *Manager) clientTools(l *live) []mcp.Tool {
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"client": {"type": "string", "description": "The client's name from list_clients."},
"client": {"type": "string", "description": "The client's name from list_clients, or its id when several are connected from that Mac."},
"path": {"type": "string", "description": "The file, absolute or starting with ~/."},
"content": {"type": "string"}
},
@@ -135,7 +135,7 @@ func (m *Manager) clientTools(l *live) []mcp.Tool {
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"client": {"type": "string", "description": "The client's name from list_clients."},
"client": {"type": "string", "description": "The client's name from list_clients, or its id when several are connected from that Mac."},
"path": {"type": "string", "description": "The file, absolute or starting with ~/."},
"old_string": {"type": "string"},
"new_string": {"type": "string"}
@@ -156,7 +156,7 @@ func (m *Manager) clientTools(l *live) []mcp.Tool {
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"client": {"type": "string", "description": "The client's name from list_clients."},
"client": {"type": "string", "description": "The client's name from list_clients, or its id when several are connected from that Mac."},
"tool": {"type": "string", "enum": ["screen_list", "capture", "ax_tree", "act", "browser", "chrome"]},
"arguments": {"type": "object", "description": "The tool's arguments."}
},
@@ -174,39 +174,26 @@ func (m *Manager) listClients(ctx context.Context) (string, error) {
if err != nil {
return "", err
}
type row struct {
c clients.Client
last *time.Time
}
rows := []row{}
for _, c := range m.clients.List() {
r := row{c: c}
for _, s := range status.Clients {
if s.Login == c.Login && s.Host == c.Host && s.Commit == c.Commit {
r.last = s.LastActionAt
}
}
rows = append(rows, r)
}
rows := status.Clients
if len(rows) == 0 {
return "No client Macs are connected.", nil
}
sort.SliceStable(rows, func(i, j int) bool {
a, b := rows[i].last, rows[j].last
a, b := rows[i].LastActionAt, rows[j].LastActionAt
return a != nil && (b == nil || a.After(*b))
})
now := time.Now()
b := &strings.Builder{}
for i, r := range rows {
fmt.Fprintf(b, "- %s (%s): ", r.c.Host, r.c.Login)
if r.last == nil {
fmt.Fprintf(b, "- %s (%s, id %s): ", r.Host, r.Login, r.Id)
if r.LastActionAt == nil {
b.WriteString("no action since byre started")
} else {
fmt.Fprintf(b, "last action %s ago", now.Sub(*r.last).Round(time.Second))
fmt.Fprintf(b, "last action %s ago", now.Sub(*r.LastActionAt).Round(time.Second))
}
fmt.Fprintf(b, ", connected %s ago, app built from %s", now.Sub(r.c.ConnectedAt).Round(time.Second), r.c.Commit)
if i == 0 && r.last != nil {
fmt.Fprintf(b, ", connected %s ago, app built from %s", now.Sub(r.ConnectedAt).Round(time.Second), r.Commit)
if i == 0 && r.LastActionAt != nil {
b.WriteString(" (most recently active)")
}
b.WriteString("\n")
+253 -250
View File
@@ -736,12 +736,15 @@ type ConnectedClient struct {
// Commit The git commit the app was built from
Commit string `json:"commit"`
// ConnectedAt When the client's oldest open stream connected.
// ConnectedAt When the client's WebSocket connected.
ConnectedAt time.Time `json:"connected_at"`
// Host The Mac's name from its Byre-Host header; empty when the client sent none.
// Host The Mac's name from its Byre-Host header.
Host string `json:"host"`
// Id The ID the app made up when it started
Id string `json:"id"`
// LastActionAt When byre last received a request the human made from this client since byre started; absent when none.
LastActionAt *time.Time `json:"last_action_at,omitempty"`
Login string `json:"login"`
@@ -1164,7 +1167,7 @@ type Status struct {
// AgentProcesses Every agent process byre is tracking, idle sessions included.
AgentProcesses []AgentProcess `json:"agent_processes"`
// Clients Each distinct user, Mac and app build with an open event stream, oldest connection first.
// Clients Each running app that holds its /api/clients/connect WebSocket, oldest connection first.
Clients []ConnectedClient `json:"clients"`
DbBytes int64 `json:"db_bytes"`
LastEventSeq int64 `json:"last_event_seq"`
@@ -3800,253 +3803,253 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
// const string: with thousands of chunks the chained `+` fold is several
// times slower for the Go compiler than parsing a slice literal.
var swaggerSpec = []string{
"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",
}
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
+97 -32
View File
@@ -22,13 +22,16 @@ const (
readLimit = 256 << 20
pingInterval = 30 * time.Second
pingTimeout = 15 * time.Second
expireAfter = 24 * time.Hour
)
type Client struct {
Login string
Host string
Commit string
ConnectedAt time.Time
ID string
Login string
Host string
Commit string
ConnectedAt time.Time
LastActionAt *time.Time
}
type Transfer struct {
@@ -38,44 +41,91 @@ type Transfer struct {
}
type conn struct {
Client
ws *websocket.Conn
done chan struct{}
mu sync.Mutex
pending map[string]chan api.ClientReply
}
type entry struct {
Client
conn *conn
seen time.Time
notified time.Time
}
type Registry struct {
mu sync.Mutex
conns map[string]*conn
clients map[string]*entry
transfers map[string]Transfer
changed func()
}
func New() *Registry {
return &Registry{conns: map[string]*conn{}, transfers: map[string]Transfer{}}
func New(changed func()) *Registry {
return &Registry{clients: map[string]*entry{}, transfers: map[string]Transfer{}, changed: changed}
}
func (r *Registry) Serve(ctx context.Context, ws *websocket.Conn, client Client) {
func (r *Registry) entry(id, login, host, commit string, now time.Time) *entry {
for k, e := range r.clients {
if e.conn == nil && now.Sub(e.seen) > expireAfter {
delete(r.clients, k)
}
}
e := r.clients[id]
if e == nil {
e = &entry{Client: Client{ID: id}}
r.clients[id] = e
}
e.Login = login
e.Host = host
e.Commit = commit
e.seen = now
return e
}
func (r *Registry) Acted(id, login, host, commit string) bool {
if id == "" {
return false
}
r.mu.Lock()
defer r.mu.Unlock()
now := time.Now()
e := r.entry(id, login, host, commit, now)
e.LastActionAt = &now
if now.Sub(e.notified) < time.Second {
return false
}
e.notified = now
return true
}
func (r *Registry) Serve(ctx context.Context, ws *websocket.Conn, id, login, host, commit string) {
ws.SetReadLimit(readLimit)
c := &conn{Client: client, ws: ws, done: make(chan struct{}), pending: map[string]chan api.ClientReply{}}
c := &conn{ws: ws, done: make(chan struct{}), pending: map[string]chan api.ClientReply{}}
r.mu.Lock()
old := r.conns[client.Host]
r.conns[client.Host] = c
now := time.Now().UTC()
e := r.entry(id, login, host, commit, now)
old := e.conn
e.conn = c
e.ConnectedAt = now
r.mu.Unlock()
if old != nil {
old.ws.Close(websocket.StatusGoingAway, "replaced by a newer connection from this host")
old.ws.Close(websocket.StatusGoingAway, "replaced by a newer connection from this client")
}
slog.Info("client connected", "host", client.Host, "login", client.Login, "commit", client.Commit)
slog.Info("client connected", "id", id, "host", host, "login", login, "commit", commit)
r.changed()
defer func() {
r.mu.Lock()
if r.conns[client.Host] == c {
delete(r.conns, client.Host)
if e.conn == c {
e.conn = nil
e.seen = time.Now()
}
r.mu.Unlock()
close(c.done)
slog.Info("client disconnected", "host", client.Host, "login", client.Login)
slog.Info("client disconnected", "id", id, "host", host, "login", login)
r.changed()
}()
ctx, cancel := context.WithCancel(ctx)
@@ -98,7 +148,7 @@ func (r *Registry) Serve(ctx context.Context, ws *websocket.Conn, client Client)
delete(c.pending, reply.Id)
c.mu.Unlock()
if ch == nil {
slog.Warn("client reply for no request", "host", client.Host, "id", reply.Id)
slog.Warn("client reply for no request", "host", host, "id", reply.Id)
continue
}
ch <- reply
@@ -128,25 +178,40 @@ func (r *Registry) List() []Client {
r.mu.Lock()
defer r.mu.Unlock()
out := []Client{}
for _, c := range r.conns {
out = append(out, c.Client)
for _, e := range r.clients {
if e.conn != nil {
out = append(out, e.Client)
}
}
sort.Slice(out, func(i, j int) bool { return out[i].Host < out[j].Host })
sort.Slice(out, func(i, j int) bool { return out[i].ConnectedAt.Before(out[j].ConnectedAt) })
return out
}
func (r *Registry) Call(ctx context.Context, host string, req api.ClientRequest) (api.ClientReply, error) {
func (r *Registry) Call(ctx context.Context, name string, req api.ClientRequest) (api.ClientReply, error) {
r.mu.Lock()
c := r.conns[host]
hosts := []string{}
for h := range r.conns {
hosts = append(hosts, h)
matches := []*conn{}
ids := []string{}
connected := []string{}
for _, e := range r.clients {
if e.conn == nil {
continue
}
connected = append(connected, fmt.Sprintf("%s (%s)", e.Host, e.ID))
if e.ID == name || e.Host == name {
matches = append(matches, e.conn)
ids = append(ids, e.ID)
}
}
r.mu.Unlock()
if c == nil {
slices.Sort(hosts)
return api.ClientReply{}, fmt.Errorf("no client named %q is connected; connected: %s", host, strings.Join(hosts, ", "))
slices.Sort(connected)
if len(matches) == 0 {
return api.ClientReply{}, fmt.Errorf("no client named %q is connected; connected: %s", name, strings.Join(connected, ", "))
}
if len(matches) > 1 {
slices.Sort(ids)
return api.ClientReply{}, fmt.Errorf("%d clients on %s are connected; name one by its id: %s", len(matches), name, strings.Join(ids, ", "))
}
c := matches[0]
req.Id = rand.Text()
ch := make(chan api.ClientReply, 1)
@@ -165,7 +230,7 @@ func (r *Registry) Call(ctx context.Context, host string, req api.ClientRequest)
}
err = c.ws.Write(ctx, websocket.MessageText, data)
if err != nil {
return api.ClientReply{}, fmt.Errorf("send to %s: %w", host, err)
return api.ClientReply{}, fmt.Errorf("send to %s: %w", name, err)
}
select {
@@ -175,9 +240,9 @@ func (r *Registry) Call(ctx context.Context, host string, req api.ClientRequest)
}
return reply, nil
case <-c.done:
return api.ClientReply{}, fmt.Errorf("%s disconnected before replying", host)
return api.ClientReply{}, fmt.Errorf("%s disconnected before replying", name)
case <-ctx.Done():
return api.ClientReply{}, fmt.Errorf("%s didn't reply: %w", host, ctx.Err())
return api.ClientReply{}, fmt.Errorf("%s didn't reply: %w", name, ctx.Err())
}
}
+4 -11
View File
@@ -10,17 +10,15 @@ import (
"os"
"os/exec"
"path/filepath"
"time"
"github.com/coder/websocket"
"g.fc.run/misc/byre/internal/clients"
)
func (s *Server) ConnectClient(w http.ResponseWriter, r *http.Request) {
id := r.Header.Get("Byre-Client")
host := r.Header.Get("Byre-Host")
if host == "" {
writeError(w, http.StatusBadRequest, errors.New("missing Byre-Host"))
if id == "" || host == "" {
writeError(w, http.StatusBadRequest, errors.New("missing Byre-Client or Byre-Host"))
return
}
@@ -31,12 +29,7 @@ func (s *Server) ConnectClient(w http.ResponseWriter, r *http.Request) {
}
defer ws.CloseNow()
s.clients.Serve(r.Context(), ws, clients.Client{
Login: actor(r),
Host: host,
Commit: r.Header.Get("Byre-Commit"),
ConnectedAt: time.Now().UTC(),
})
s.clients.Serve(r.Context(), ws, id, actor(r), host, r.Header.Get("Byre-Commit"))
}
func (s *Server) GetClientTransfer(w http.ResponseWriter, r *http.Request, transferID string) {
+5 -49
View File
@@ -1,45 +1,25 @@
package server
import (
"sort"
"sync"
"time"
"g.fc.run/misc/byre/internal/api"
)
type clientKey struct{ login, host, commit string }
type hub struct {
mu sync.Mutex
subs map[chan api.Event]api.ConnectedClient
lastAction map[clientKey]time.Time
lastNotified map[clientKey]time.Time
mu sync.Mutex
subs map[chan api.Event]bool
}
func newHub() *hub {
return &hub{subs: map[chan api.Event]api.ConnectedClient{}, lastAction: map[clientKey]time.Time{}, lastNotified: map[clientKey]time.Time{}}
return &hub{subs: map[chan api.Event]bool{}}
}
func (h *hub) acted(login, host, commit string) bool {
h.mu.Lock()
defer h.mu.Unlock()
k := clientKey{login, host, commit}
now := time.Now()
h.lastAction[k] = now
prev, ok := h.lastNotified[k]
if ok && now.Sub(prev) < time.Second {
return false
}
h.lastNotified[k] = now
return true
}
func (h *hub) subscribe(client api.ConnectedClient) chan api.Event {
func (h *hub) subscribe() chan api.Event {
h.mu.Lock()
defer h.mu.Unlock()
ch := make(chan api.Event, 256)
h.subs[ch] = client
h.subs[ch] = true
return ch
}
@@ -72,27 +52,3 @@ func (h *hub) count() int {
defer h.mu.Unlock()
return len(h.subs)
}
func (h *hub) clients() []api.ConnectedClient {
h.mu.Lock()
defer h.mu.Unlock()
oldest := map[clientKey]api.ConnectedClient{}
for _, c := range h.subs {
k := clientKey{c.Login, c.Host, c.Commit}
prev, ok := oldest[k]
if ok && !c.ConnectedAt.Before(prev.ConnectedAt) {
continue
}
oldest[k] = c
}
out := []api.ConnectedClient{}
for k, c := range oldest {
at, ok := h.lastAction[k]
if ok {
c.LastActionAt = &at
}
out = append(out, c)
}
sort.Slice(out, func(i, j int) bool { return out[i].ConnectedAt.Before(out[j].ConnectedAt) })
return out
}
+18 -3
View File
@@ -45,11 +45,11 @@ func New(dataDir string, portFirst int) (*Server, error) {
s := &Server{
hub: newHub(),
clients: clients.New(),
terminals: newTerminals(),
startedAt: time.Now().UTC(),
users: map[string]string{},
}
s.clients = clients.New(s.clientsChanged)
s.store, err = store.Open(filepath.Join(dataDir, "byre.db"), portFirst, s.publish)
if err != nil {
@@ -145,11 +145,26 @@ func (s *Server) identify(next http.Handler) http.Handler {
}
func (s *Server) acted(login string, r *http.Request) {
if s.hub.acted(login, r.Header.Get("Byre-Host"), r.Header.Get("Byre-Commit")) {
if s.clients.Acted(r.Header.Get("Byre-Client"), login, r.Header.Get("Byre-Host"), r.Header.Get("Byre-Commit")) {
s.clientsChanged()
}
}
func (s *Server) connectedClients() []api.ConnectedClient {
out := []api.ConnectedClient{}
for _, c := range s.clients.List() {
out = append(out, api.ConnectedClient{
Id: c.ID,
Login: c.Login,
Host: c.Host,
Commit: c.Commit,
ConnectedAt: c.ConnectedAt,
LastActionAt: c.LastActionAt,
})
}
return out
}
func (s *Server) clientsChanged() {
s.store.Broadcast(api.Event{Kind: "admin.clients", Actor: "byre", Payload: map[string]any{}})
}
@@ -638,7 +653,7 @@ func (s *Server) status(ctx context.Context) (api.Status, error) {
Projects: c.Projects,
ActiveTasks: c.ActiveTasks,
SseClients: s.hub.count(),
Clients: s.hub.clients(),
Clients: s.connectedClients(),
LiveAgents: s.agents.LiveCount(),
RunningTurns: usage.Running,
RateLimits: usage.RateLimits,
+1 -6
View File
@@ -55,12 +55,7 @@ func (s *Server) StreamEvents(w http.ResponseWriter, r *http.Request, params api
w.Header().Set("Cache-Control", "no-cache")
w.WriteHeader(http.StatusOK)
live := s.hub.subscribe(api.ConnectedClient{
Login: viewer,
Host: r.Header.Get("Byre-Host"),
Commit: r.Header.Get("Byre-Commit"),
ConnectedAt: time.Now(),
})
live := s.hub.subscribe()
defer s.clientsChanged()
defer s.hub.unsubscribe(live)
s.clientsChanged()