Two related daemon-side status-stream fixes that together keep the UI's
status in sync with the daemon's contextState:
* state.Set previously only mutated the in-memory enum — transitions
that weren't accompanied by a Mark{Management,Signal,...} call (e.g.
StatusNeedsLogin after a PermissionDenied login, StatusLoginFailed
after OAuth init failure, StatusIdle in the Login defer) left the
UI stuck on the previous snapshot until an unrelated peer event
happened to fire notifyStateChange. Add a callback on contextState
fired from Set (outside the mutex, to avoid lock-order issues with
the recorder's stateChangeMux), and wire it in Server.Start to the
recorder's new public NotifyStateChange. Every state.Set callsite
now pushes automatically; new ones don't need to opt in.
* WaitSSOLogin's WaitToken error branch lumped every failure into
StatusLoginFailed, including context.Canceled aborts from a parallel
profile switch (actCancel/waitCancel). That spurious LoginFailed
then wedged the new profile's Up RPC with "up already in progress:
current status LoginFailed". Split the branch by error type:
context.Canceled lets the top-level defer pick StatusIdle,
context.DeadlineExceeded sets StatusNeedsLogin (retryable; OAuth
device-code window just expired), other errors keep LoginFailed
(real auth/IO failures). Document the full state-transition table
in the function godoc.
The status snapshot tore down on every management retry because
state.Status() blanks the status when an error is wrapped, and the
SubscribeStatus stream propagated that as FailedPrecondition. The UI
treated any stream error as "daemon not running" and flickered the tray
to Not running between retries.
Disconnect was also unresponsive: Down set Idle before the retry
goroutine exited, which then overwrote it with Set(Connecting) on the
next attempt; the backoff sleep (up to 15s) wasn't context-aware, so the
goroutine kept running long after actCancel.
- buildStatusResponse falls back to the underlying status (via new
state.CurrentStatus) instead of breaking the stream on wrapped errors.
- UI only flips to DaemonUnavailable on codes.Unavailable / non-status
errors, so a live daemon returning FailedPrecondition is not reported
as down.
- connect retry uses backoff.WithContext so actCancel interrupts the
inter-attempt sleep, and skips Wrap(err) when the dial fails due to
ctx cancellation.
- Down sets Idle after waiting for giveUpChan, so the retry goroutine
can no longer race the disconnect.
- Tray hides Connect during Connecting and keeps Disconnect enabled so
the user can abort an in-flight connection attempt.
This PR implements a feature enhancement to display a login popup when the session expires. Key changes include updating flag handling and client construction to support a new login URL popup, revising login and notification handling logic to use the new popup, and updating status and server-side session state management accordingly.
UI and CLI Clients are now able to use SSO login by default
we will check if the management has configured or supports SSO providers
daemon will handle fetching and waiting for an access token
Oauth package was moved to internal to avoid one extra package at this stage
Secrets were removed from OAuth
CLI clients have less and better output
2 new status were introduced, NeedsLogin and FailedLogin for better messaging
With NeedsLogin we no longer have endless login attempts