Something went wrong. Try again.
small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217// Package app owns daemon-backed workflows without terminal interaction or rendering.package app
import ( "context" "errors" "fmt" "net/http" "path/filepath" "regexp" "slices" "strings"
"albedo/cli/internal/config" "albedo/cli/internal/daemon")
// Service obtains connections only when an operation needs one.type Service struct { Connect func(context.Context) (*daemon.Connection, error) Existing func(context.Context) (*daemon.Connection, error)}type OpenOptions struct { SessionID, Workspace string Fresh, Terminal bool}type PreparedOpen struct { Connection *daemon.Connection Providers config.Profiles Selected *daemon.Session Workspace string Sessions []daemon.Session LoginRequired bool}
// absoluteWorkspace resolves a local workspace against the current// directory; a location on another host is the daemon's to read.func absoluteWorkspace(workspace string) string { if host, _ := daemon.SplitLocation(workspace); host != "" { return workspace } if abs, err := filepath.Abs(workspace); err == nil { return abs } return workspace}
func (s *Service) PrepareOpen(ctx context.Context, options OpenOptions) (PreparedOpen, error) { id, workspace, fresh := options.SessionID, options.Workspace, options.Fresh absWorkspace := absoluteWorkspace(workspace)
conn, err := s.Connect(ctx) if err != nil { return PreparedOpen{}, err }
sessions, err := daemon.ListSessions(ctx, conn) if err != nil { return PreparedOpen{}, err }
var selected *daemon.Session if id != "" { session, matchErr := matchSession(sessions, id) if matchErr != nil { return PreparedOpen{}, matchErr } selected = &session }
profs, err := daemon.ProviderProfiles(ctx, conn) if err != nil { return PreparedOpen{}, err } configured := profs.Active != ""
if !configured && !options.Terminal { return PreparedOpen{}, errors.New("no model provider is configured; run albedo login in a terminal to set one up") }
var initial *daemon.Session if selected != nil { captured, err := daemon.GetSession(ctx, conn, selected.ID) if err != nil { return PreparedOpen{}, err } initial = &captured } else if configured && (fresh || len(sessions) == 0) { created, createErr := daemon.CreateSession(ctx, conn, daemon.CreateSessionRequest{Workspace: absWorkspace}) if createErr != nil { return PreparedOpen{}, createErr } initial = &created }
return PreparedOpen{Connection: conn, Providers: profs, Sessions: sessions, Selected: initial, Workspace: absWorkspace, LoginRequired: !configured}, nil}
func (s *Service) Sessions(ctx context.Context) ([]daemon.Session, error) { conn, err := s.Connect(ctx) if err != nil { return nil, err } sessions, err := daemon.ListSessions(ctx, conn) if err != nil { return nil, err } return slices.DeleteFunc(sessions, func(session daemon.Session) bool { return session.Archived }), nil}
// matchSession finds the session whose ID is id or, failing that, the only one// that starts with it, since `albedo sessions` prints shortened IDs.func matchSession(sessions []daemon.Session, id string) (daemon.Session, error) { selectedIndex, matchCount := -1, 0 for i := range sessions { if sessions[i].ID == id { return sessions[i], nil } if strings.HasPrefix(sessions[i].ID, id) { selectedIndex = i matchCount++ } } switch matchCount { case 0: return daemon.Session{}, fmt.Errorf("no session matches %q; run albedo sessions to see the available sessions", id) case 1: return sessions[selectedIndex], nil default: return daemon.Session{}, fmt.Errorf("more than one session ID starts with %q; use a longer ID or run albedo sessions to find it", id) }}
// The UUIDv7 shape in the daemon's operation validator and OpenAPI schema.var fullSessionID = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`)
// resolveSession reads full IDs directly and expands prefixes against root sessions.func resolveSession(ctx context.Context, conn *daemon.Connection, id string) (string, error) { if fullSessionID.MatchString(id) { configuration, err := daemon.GetSessionConfiguration(ctx, conn, id) if problem, ok := errors.AsType[*daemon.APIError](err); ok && problem.StatusCode == http.StatusNotFound { return "", fmt.Errorf("no session matches %q; run albedo sessions to see the available sessions", id) } return configuration.Value.ID, err } sessions, err := daemon.ListSessions(ctx, conn) if err != nil { return "", err } session, err := matchSession(sessions, id) return session.ID, err}
func (s *Service) Send(ctx context.Context, id, prompt string) (daemon.SendResult, error) { conn, err := s.Connect(ctx) if err != nil { return daemon.SendResult{}, err } id, err = resolveSession(ctx, conn, id) if err != nil { return daemon.SendResult{}, err } return daemon.Submit(ctx, conn, id, daemon.SubmissionRequest{Content: &prompt})}
type InterruptionResult struct { Interrupted bool `json:"interrupted"`}
func (s *Service) Stop(ctx context.Context, id string) (InterruptionResult, error) { conn, err := s.Connect(ctx) if err != nil { return InterruptionResult{}, err } id, err = resolveSession(ctx, conn, id) if err != nil { return InterruptionResult{}, err } interrupted, err := daemon.InterruptSession(ctx, conn, id) return InterruptionResult{Interrupted: interrupted}, err}func (s *Service) DeleteSessions(ctx context.Context, ids []string) error { conn, err := s.Connect(ctx) if err != nil { return err } for _, id := range ids { configuration, err := daemon.GetSessionConfiguration(ctx, conn, id) if err != nil { return err } result, err := daemon.DeleteSession(ctx, conn, id, false, daemon.SessionCondition{ETag: configuration.ETag}) if err != nil { return err } if !result.OK { return errors.New(result.Message) } } return nil}func (s *Service) StopDaemon(ctx context.Context) error { conn, err := s.Existing(ctx) if err != nil || conn == nil { return err } return daemon.StopDaemon(ctx, conn)}func (s *Service) PrepareLogin(ctx context.Context) (PreparedOpen, error) { conn, err := s.Connect(ctx) if err != nil { return PreparedOpen{}, err } profiles, err := daemon.ProviderProfiles(ctx, conn) return PreparedOpen{Connection: conn, Providers: profiles}, err}