package session import ( "crypto/rand" "encoding/hex" "fmt" "sort" "sync" ) // Registry is the daemon's set of live sessions. type Registry struct { mu sync.RWMutex sessions map[string]*Session } func NewRegistry() *Registry { return &Registry{sessions: make(map[string]*Session)} } // Create starts a new session and registers it under a fresh ID. func (r *Registry) Create(opts Options) (*Session, error) { r.mu.Lock() defer r.mu.Unlock() var id string for { b := make([]byte, 3) if _, err := rand.Read(b); err != nil { return nil, fmt.Errorf("generating session id: %w", err) } id = hex.EncodeToString(b) if _, taken := r.sessions[id]; !taken { break } } s, err := New(id, opts) if err != nil { return nil, err } r.sessions[id] = s return s, nil } // Get returns the session or nil. func (r *Registry) Get(id string) *Session { r.mu.RLock() defer r.mu.RUnlock() return r.sessions[id] } // List returns all sessions sorted by ID. func (r *Registry) List() []*Session { r.mu.RLock() defer r.mu.RUnlock() out := make([]*Session, 0, len(r.sessions)) for _, s := range r.sessions { out = append(out, s) } sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID }) return out } // Remove unregisters a session (the caller is responsible for Kill). func (r *Registry) Remove(id string) { r.mu.Lock() defer r.mu.Unlock() delete(r.sessions, id) }