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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256package daemon
import ( "context" "encoding/json" "errors" "io" "net/http" "net/url" "time"
"albedo/cli/internal/config" "albedo/cli/internal/daemon/protocol")
type SignIn struct { Provider, Label, Detail, Protocol, Kind string Flows []string Fields []FormField}type Account struct { Provider, ID, Label, Detail, Kind, Source, LimitedUntil string Endpoint *string RotatedByProfiles []string SelectedByProfiles []string Selected bool}type SignIns struct { Logins []SignIn Accounts []Account}type StartedSignIn struct{ ID, URL, ETag string }type SignInStatus struct { State, Message, ETag, Instructions, URL, Provider string Accounts []Account}
func accountValue(w protocol.Account) Account { return Account{Provider: string(w.Provider), ID: string(w.ID), Label: w.Label, Detail: w.Detail, Kind: w.Kind, Source: w.Source, Endpoint: w.Endpoint, LimitedUntil: value(w.LimitedUntil), RotatedByProfiles: idsToStrings(w.RotatedByProfiles), Selected: len(w.SelectedByProfiles) > 0, SelectedByProfiles: idsToStrings(w.SelectedByProfiles)}}func idsToStrings(ids []protocol.ID) []string { if len(ids) == 0 { return nil } result := make([]string, len(ids)) for i, id := range ids { result[i] = string(id) } return result}
// PutKeyAccount stores a key under a client-generated UUIDv7.func PutKeyAccount(ctx context.Context, conn *Connection, provider, endpoint, key, label string) (Account, error) { id, opErr := operationID() if opErr != nil { return Account{}, opErr } body := protocol.KeyAccountRequest{Provider: protocol.ID(provider), APIKey: &key, Endpoint: endpoint, Label: optionalText(label)} var result Account err := executeMutation(ctx, conn, operation{Capability: "provider_auth", Name: "save API key", BuildRequest: func(base string, body io.Reader) (*http.Request, error) { return protocol.NewPutKeyAccountRequestWithBody(base, protocol.UUIDv7(id), "application/json", body) }, Body: body, Policy: noRecovery}, []int{200, 201}, func(data []byte, _ int) error { var wire protocol.Account if err := decodeRequired(data, &wire); err != nil { return err } result = accountValue(wire) return nil }) return result, err}
// RenameAccount changes a saved account label; the daemon rejects env accounts.func RenameAccount(ctx context.Context, conn *Connection, id, label string) (Account, error) { var result Account err := executeMutation(ctx, conn, operation{Capability: "provider_auth", Name: "rename provider account", BuildRequest: func(base string, body io.Reader) (*http.Request, error) { return protocol.NewPatchAccountRequestWithBody(base, id, "application/json", body) }, Body: protocol.AccountPatch{Label: label}, Policy: noRecovery}, []int{200}, func(data []byte, _ int) error { var wire protocol.Account if err := decodeRequired(data, &wire); err != nil { return err } result = accountValue(wire) return nil }) return result, err}
func SignInList(ctx context.Context, conn *Connection) (SignIns, error) { var result SignIns err := executeRead(ctx, conn, operation{Capability: "provider_auth", Name: "list provider accounts", BuildRequest: func(base string, body io.Reader) (*http.Request, error) { return protocol.NewGetAuthRequest(base) }, Policy: readRecovery}, func(data []byte) error { var w protocol.Auth if err := json.Unmarshal(data, &w); err != nil { return err } if w.Providers == nil || w.Accounts == nil { return fieldError("auth") } result = SignIns{Logins: []SignIn{}, Accounts: []Account{}} for _, p := range w.Providers { result.Logins = append(result.Logins, SignIn{Provider: p.ID, Label: p.Label, Detail: p.Detail, Kind: p.Kind, Flows: p.Flows, Fields: p.Fields}) } for _, a := range w.Accounts { result.Accounts = append(result.Accounts, accountValue(a)) } return nil }) return result, err}func loginValue(w protocol.Login, etag string) (SignInStatus, error) { if w.ID == "" || etag == "" || w.Accounts == nil { return SignInStatus{}, fieldError("login") } switch w.State { case "waiting", "exchanging", "complete", "failed", "cancelled", "expired": default: return SignInStatus{}, fieldError("login state") } if urlText := value(w.URL); urlText != "" { parsed, err := url.Parse(urlText) if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") { return SignInStatus{}, fieldError("login URL") } } r := SignInStatus{State: w.State, Message: w.Progress, ETag: etag, Instructions: value(w.Instructions), URL: value(w.URL), Provider: w.Provider, Accounts: []Account{}} if w.Failure != nil { r.Message = w.Failure.Detail } for _, a := range w.Accounts { r.Accounts = append(r.Accounts, accountValue(a)) } return r, nil}
// signInTimeout outlasts the daemon's own 60 s wait for its sign-in owner, so a// busy owner reads as the daemon's timeout, not the client's.const signInTimeout = 70 * time.Second
// StartSignInWithID retains the same admitted intent when a transport failure is retried.func StartSignInWithID(ctx context.Context, conn *Connection, id, provider, flow string, values map[string]json.RawMessage) (StartedSignIn, error) { result := StartedSignIn{ID: id} body := protocol.LoginRequest{Provider: provider, Flow: optionalText(flow), Values: nonNilMap(values)} frozen, marshalErr := json.Marshal(body) if marshalErr != nil { return result, marshalErr } op := operation{Capability: "provider_auth", Name: "start provider login", BuildRequest: func(base string, body io.Reader) (*http.Request, error) { return protocol.NewCreateLoginRequestWithBody(base, id, "application/json", body) }, Body: json.RawMessage(frozen), Validator: &result.ETag, Policy: noRecovery, Timeout: signInTimeout} for attempt := range 2 { err := executeMutation(ctx, conn, op, []int{201, 200}, func(data []byte, _ int) error { var w protocol.Login if err := json.Unmarshal(data, &w); err != nil { return err } if w.ID != id || w.Provider != provider { return fieldError("login identity") } status, err := loginValue(w, result.ETag) if err != nil { return err } result.URL = status.URL return nil }) if err == nil { return result, nil } if _, uncertain := errors.AsType[*UncertainOutcomeError](err); !uncertain { return result, err } if ctx != nil && ctx.Err() != nil { return result, err } status, lookupErr := PollSignIn(ctx, conn, id) if lookupErr == nil { if status.Provider != provider { return result, fieldError("login provider") } result.URL, result.ETag = status.URL, status.ETag return result, nil } if api, ok := errors.AsType[*APIError](lookupErr); ok && api.StatusCode == 410 { return result, lookupErr } if attempt == 1 { return result, err } } panic("unreachable login retry budget")}func PollSignIn(ctx context.Context, conn *Connection, id string) (SignInStatus, error) { var result SignInStatus var etag string err := executeRead(ctx, conn, operation{Capability: "provider_auth", Name: "read provider login", BuildRequest: func(base string, body io.Reader) (*http.Request, error) { return protocol.NewGetLoginRequest(base, id) }, Validator: &etag, Policy: readRecovery, Timeout: signInTimeout}, func(data []byte) error { var w protocol.Login if err := json.Unmarshal(data, &w); err != nil { return err } if w.ID != id { return fieldError("login identity") } var err error result, err = loginValue(w, etag) return err }) return result, err}func SignInInput(ctx context.Context, conn *Connection, id, input, etag string) error { headers, err := observedHeaders(etag) if err != nil { return err } var next string return executeMutation(ctx, conn, operation{Capability: "provider_auth", Name: "answer provider login", BuildRequest: func(base string, body io.Reader) (*http.Request, error) { return protocol.NewAnswerLoginRequestWithBody(base, id, &protocol.AnswerLoginParams{IfMatch: etag}, "application/merge-patch+json", body) }, Body: protocol.LoginPatch{Response: &input}, Headers: headers, Validator: &next, Policy: noRecovery, Timeout: signInTimeout}, []int{200}, func(data []byte, _ int) error { var w protocol.Login if err := json.Unmarshal(data, &w); err != nil { return err } _, err := loginValue(w, next) return err })}func CancelSignIn(ctx context.Context, conn *Connection, id string) error { return executeMutation(ctx, conn, operation{Capability: "provider_auth", Name: "cancel provider login", BuildRequest: func(base string, body io.Reader) (*http.Request, error) { return protocol.NewCancelLoginRequest(base, id) }, Policy: noRecovery, Timeout: signInTimeout}, []int{204}, func([]byte, int) error { return nil })}func SelectProfileAccount(ctx context.Context, conn *Connection, profile string, account Account, profiles config.Profiles) error { saved, ok := profiles.Providers[profile] if !ok || saved.Extension != account.Provider { return errors.New("select a profile for this account provider") } saved.AccountID = &account.ID saved.AccountIDs = nil if account.Endpoint != nil && *account.Endpoint != "" { saved.BaseURL = *account.Endpoint } return SaveProvider(ctx, conn, profile, saved, profiles.ETag)}func RemoveAccount(ctx context.Context, conn *Connection, id string) error { return executeMutation(ctx, conn, operation{Capability: "provider_auth", Name: "remove provider account", BuildRequest: func(base string, body io.Reader) (*http.Request, error) { return protocol.NewDeleteAccountRequest(base, id) }, Policy: noRecovery}, []int{204}, func([]byte, int) error { return nil })}
func StartProviderLogin(ctx context.Context, conn *Connection, provider, flow string, values map[string]json.RawMessage) (StartedSignIn, error) { id, err := operationID() if err != nil { return StartedSignIn{}, err } return StartSignInWithID(ctx, conn, id, provider, flow, values)}