## Summary
- Add `teamWorkspaceStore` Pinia store for workspace state management
(workspaces, members, invites, current workspace)
- Add `workspaceApi` client for workspace CRUD, member management, and
invite operations
- Update `useWorkspaceSwitch` composable for workspace switching logic
- Update `useSessionCookie` for workspace-aware sessions
- Update `firebaseAuthStore` for workspace aware auth
- Use `workspaceAuthStore` for workspace auth flow
## Test plan
- [x] 59 unit tests passing (50 store tests + 9 switch tests)
- [x] Typecheck passing
- [x] Lint passing
- [x] Knip passing
Note: This PR depends on the `team_workspaces_enabled` feature flag
being available (already in main).
🤖 Generated with [Claude Code](https://claude.ai/code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8194-feat-add-workspace-session-auth-and-store-infrastructure-2ef6d73d3650814984afe8ee7ba0a209)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
Pass target tier to billing portal API for deep linking to Stripe's
subscription update confirmation screen when user has an active
subscription.
## Changes
- **What**: When a user with an active subscription clicks a tier in
PricingTable, pass the target tier (including billing cycle) to
`accessBillingPortal` which sends it as `target_tier` in the request
body. This enables the backend to create a Stripe billing portal deep
link directly to the subscription update confirmation screen.
- **Dependencies**: Requires comfy-api PR for `POST /customers/billing`
`target_tier` support
## Review Focus
- PricingTable now differentiates between new subscriptions (checkout
flow) and existing subscriptions (billing portal with deep link)
- Type derivation uses `Parameters<typeof
authStore.accessBillingPortal>[0]` to avoid duplicating the tier union
(matches codebase pattern)
- Registry types manually updated to include `target_tier` field (will
be regenerated when API is deployed)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7692-feat-pass-target-tier-to-billing-portal-for-subscription-updates-2d06d73d365081b38fe4c81e95dce58c)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
Fixes a race causing “No auth header available for session creation”
during sign‑in, by skipping the initial token refresh event, and
wrapping extension auth hooks with async error handling.
Sentry:
https://comfy-org.sentry.io/issues/6990347926/?alert_rule_id=1614600&project=4509681221369857
Context
- Error surfaced as an unhandled rejection when session creation was
triggered without a valid auth header.
- Triggers: both onAuthUserResolved and onAuthTokenRefreshed fired
during initial login.
- Pre‑fix, onIdTokenChanged treated the very first token emission as a
“refresh” as well, so two concurrent createSession() calls ran
back‑to‑back.
- One of those calls could land before a Firebase ID token existed, so
getAuthHeader() returned null → createSession threw “No auth header
available for session creation”.
Exact pre‑fix failure path
- src/extensions/core/cloudSessionCookie.ts
- onAuthUserResolved → useSessionCookie().createSession()
- onAuthTokenRefreshed → useSessionCookie().createSession()
- src/stores/firebaseAuthStore.ts
- onIdTokenChanged increments tokenRefreshTrigger even for the initial
token (treated as a refresh)
- getAuthHeader() → getIdToken() may be undefined briefly during
initialization
- src/platform/auth/session/useSessionCookie.ts
- createSession(): calls authStore.getAuthHeader(); if falsy, throws
Error('No auth header available for session creation')
What this PR changes
1) Skip initial token “refresh”
- Track lastTokenUserId and ignore the first onIdTokenChanged for a
user; only subsequent token changes count as refresh events.
- File: src/stores/firebaseAuthStore.ts
2) Wrap extension auth hooks with async error handling
- Use wrapWithErrorHandlingAsync for
onAuthUserResolved/onAuthTokenRefreshed/onAuthUserLogout callbacks to
avoid unhandled rejections.
- File: src/services/extensionService.ts
Result
- Eliminates the timing window where createSession() runs before
getIdToken() returns a token.
- Ensures any remaining errors are caught and reported instead of
surfacing as unhandled promise rejections.
Notes
- Lint and typecheck run clean (pnpm lint:fix && pnpm typecheck).
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6563-Fix-session-cookie-creation-race-dedupe-calls-skip-initial-token-refresh-wrap-extensio-2a16d73d365081ef8c22c5ac8cb948aa)
by [Unito](https://www.unito.io)
## Summary
This code is entirely excluded from open-source, local, and desktop
builds. During minification and dead-code elimination, the Mixpanel
library is fully tree-shaken -- meaning no telemetry code is ever
included or downloaded in those builds. Even the inline callsites are
removed during the build (because `isCloud` becomes false and the entire
block becomes dead code and is removed). The code not only has no
effect, is not even distributed in the first place. We’ve gone to great
lengths to ensure this behavior.
Verification proof:
https://github.com/user-attachments/assets/b66c35f7-e233-447f-93da-4d70c433908d
Telemetry is *enabled only in the ComfyUI Cloud environment*. Its goal
is to help us understand and improve onboarding and new-user adoption.
ComfyUI aims to be accessible to everyone, but we know the learning
curve can be steep. Anonymous usage insights will help us identify where
users struggle and guide us toward making the experience more intuitive
and welcoming.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6154-add-telemetry-provider-for-cloud-distribution-2926d73d3650813cb9ccfb3a2733848b)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Summary
Implements cloud subscription management UI and flow for ComfyUI Cloud
users.
Core Features:
- Subscription Status Tracking: Global reactive state management for
subscription status across all components
using shared subscriptionStatus ref
- Subscribe to Run Button: Replaces the Run button in the actionbar with
a "Subscribe to Run" button for users
without active subscriptions
- Subscription Required Dialog: Modal dialog with subscription benefits,
pricing, and checkout flow with video
background
- Subscription Settings Panel: New settings panel showing subscription
status, renewal date, and quick access to
billing management
- Auto-detection & Polling: Automatically polls subscription status
after checkout completion and syncs state
across the application
https://github.com/user-attachments/assets/f41b8e6a-5845-48a7-8169-3a6fc0d2e5c8
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6064-subscription-page-28d6d73d36508135a2a0fe7c94b40852)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
## Summary
This reverts PR #5614 which moved VueFire persistence configuration to
initialization.
## Reason for Revert
It breaks Google SSO login with error:
```
useErrorHandling.ts:12 FirebaseError: Firebase: Error (auth/argument-error).
at createErrorInternal (index-c92d61ad.js:506:41)
at _assert (index-c92d61ad.js:512:15)
at _withDefaultResolver (index-c92d61ad.js:9237:5)
at signInWithPopup (index-c92d61ad.js:9457:30)
at executeAuthAction.createCustomer (firebaseAuthStore.ts:263:25)
at executeAuthAction (firebaseAuthStore.ts:223:28)
at Proxy.loginWithGoogle (firebaseAuthStore.ts:262:5)
at Proxy.wrappedAction (pinia.mjs:1405:26)
at useFirebaseAuthActions.ts:104:28
at Object.signInWithGoogle (useErrorHandling.ts:39:22)
```
## Changes
- Reverts commit ea4e57b60 "Move VueFire persistence configuration to
initialization (#5614)"
- Restores previous Firebase auth persistence behavior
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5729-Revert-Move-VueFire-persistence-configuration-to-initialization-5614-2776d73d3650814c9b80d9c67c852874)
by [Unito](https://www.unito.io)
## Summary
Some users were authenticating successfully but their email addresses
weren't being extracted from the Firebase token. This happened because
we weren't explicitly requesting the email scope during OAuth
authentication.
While Firebase's default configuration includes basic profile info, it
doesn't guarantee email access for all account types - particularly
Google Workspace accounts with restrictive policies or users with
privacy-conscious settings.
[Github
Scopes](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps)
## Changes
Adding email scope for Google + Github social OAuth.
## Review Focus
N/A
## Screenshots (if applicable)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5638-Explicitly-add-email-scope-for-social-auth-login-2726d73d3650817ab356fc9c04f8641b)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
* knip: Don't ignore exports that are only used within a given file
* knip: More pruning after rebase
* knip: Vite plugin config fix
* knip: vitest plugin config
* knip: Playwright config, remove unnecessary ignores.
* knip: Simplify project file enumeration.
* knip: simplify the config file patterns ?(.optional_segment)
* knip: tailwind v4 fix
* knip: A little more, explain some of the deps.
Should be good for this PR.
* knip: remove unused disabling of classMembers.
It's opt-in, which we should probably do.
* knip: floating comments
We should probably delete _one_ of these parallell trees, right?
* knip: Add additional entrypoints
* knip: Restore UserData that's exposed via the types for now.
* knip: Add as an entry file even though knip says it's not necessary.
* knip: re-export functions used by nodes (h/t @christian-byrne)
* [fix] gracefully handle Firebase auth failure
* [test] Add failing tests to reproduce Firebase Auth network issue #4468
Add test cases that demonstrate the current problematic behavior where
Firebase Auth makes network requests when offline without graceful error
handling, causing toast error messages and degraded offline experience.
Tests reproduce:
- getIdToken() throwing auth/network-request-failed instead of returning null
- getAuthHeader() failing to fallback gracefully when Firebase token refresh fails
These tests currently pass by expecting the error to be thrown. After
implementing the fix, the tests should be updated to verify graceful
handling (returning null instead of throwing).
Related to issue #4468: Firebase Auth makes network requests when offline
without evicting token
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
* [test] update firebaseAuthStore tests
They match the behavior of the implemented solution now
* [test] add firebaseAuthStore.getTokenId test for non-network errors
* [chore] code review feedback
* [test] use FirebaseError
Co-authored-by: Alexander Brown <drjkl@comfy.org>
* [fix] remove indentation and fix test
---------
Co-authored-by: snomiao <snomiao@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>