Cloud: bugfixes: log refresh errors and allow logout from inactive-session (#4268)

* Cloud: log session refresh errors

* Cloud: bugfix: allow logouts during the inactive-session state
This commit is contained in:
John Richmond 2025-06-02 15:32:36 -07:00 committed by GitHub
parent 900de7b401
commit 2fda064ae9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 68 additions and 28 deletions

View file

@ -11,6 +11,7 @@ import { getClerkBaseUrl, getRooCodeApiUrl } from "./Config"
import { RefreshTimer } from "./RefreshTimer"
export interface AuthServiceEvents {
"inactive-session": [data: { previousState: AuthState }]
"active-session": [data: { previousState: AuthState }]
"logged-out": [data: { previousState: AuthState }]
"user-info": [data: { userInfo: CloudUserInfo }]
@ -92,11 +93,15 @@ export class AuthService extends EventEmitter<AuthServiceEvents> {
private transitionToInactiveSession(credentials: AuthCredentials): void {
this.credentials = credentials
const previousState = this.state
this.state = "inactive-session"
this.sessionToken = null
this.userInfo = null
this.emit("inactive-session", { previousState })
this.timer.start()
console.log("[auth] Transitioned to inactive-session state")
@ -281,14 +286,19 @@ export class AuthService extends EventEmitter<AuthServiceEvents> {
return
}
const previousState = this.state
this.sessionToken = await this.clerkCreateSessionToken()
this.state = "active-session"
try {
const previousState = this.state
this.sessionToken = await this.clerkCreateSessionToken()
this.state = "active-session"
if (previousState !== "active-session") {
console.log("[auth] Transitioned to active-session state")
this.emit("active-session", { previousState })
this.fetchUserInfo()
if (previousState !== "active-session") {
console.log("[auth] Transitioned to active-session state")
this.emit("active-session", { previousState })
this.fetchUserInfo()
}
} catch (error) {
console.error("[auth] Failed to refresh session", error)
throw error
}
}

View file

@ -35,6 +35,7 @@ export class CloudService {
try {
this.authService = await AuthService.createInstance(this.context)
this.authService.on("inactive-session", this.authListener)
this.authService.on("active-session", this.authListener)
this.authService.on("logged-out", this.authListener)
this.authService.on("user-info", this.authListener)

View file

@ -1300,6 +1300,7 @@ export class ClineProvider
terminalCompressProgressBar,
historyPreviewCollapsed,
cloudUserInfo,
cloudIsAuthenticated,
organizationAllowList,
maxConcurrentFileReads,
condensingApiConfigId,
@ -1398,6 +1399,7 @@ export class ClineProvider
hasSystemPromptOverride,
historyPreviewCollapsed: historyPreviewCollapsed ?? false,
cloudUserInfo,
cloudIsAuthenticated: cloudIsAuthenticated ?? false,
organizationAllowList,
condensingApiConfigId,
customCondensingPrompt,
@ -1453,6 +1455,16 @@ export class ClineProvider
)
}
let cloudIsAuthenticated: boolean = false
try {
cloudIsAuthenticated = CloudService.instance.isAuthenticated()
} catch (error) {
console.error(
`[getState] failed to get cloud authentication state: ${error instanceof Error ? error.message : String(error)}`,
)
}
// Return the same structure as before
return {
apiConfiguration: providerSettings,
@ -1528,6 +1540,7 @@ export class ClineProvider
: 1,
historyPreviewCollapsed: stateValues.historyPreviewCollapsed ?? false,
cloudUserInfo,
cloudIsAuthenticated,
organizationAllowList,
// Explicitly add condensing settings
condensingApiConfigId: stateValues.condensingApiConfigId,

View file

@ -427,6 +427,7 @@ describe("ClineProvider", () => {
organizationAllowList: ORGANIZATION_ALLOW_ALL,
autoCondenseContext: true,
autoCondenseContextPercent: 100,
cloudIsAuthenticated: false,
}
const message: ExtensionMessage = {

View file

@ -219,6 +219,7 @@ export type ExtensionState = Pick<
historyPreviewCollapsed?: boolean
cloudUserInfo: CloudUserInfo | null
cloudIsAuthenticated: boolean
organizationAllowList: OrganizationAllowList
autoCondenseContext: boolean

View file

@ -37,6 +37,7 @@ const App = () => {
telemetryKey,
machineId,
cloudUserInfo,
cloudIsAuthenticated,
} = useExtensionState()
const [showAnnouncement, setShowAnnouncement] = useState(false)
@ -127,7 +128,13 @@ const App = () => {
{tab === "settings" && (
<SettingsView ref={settingsRef} onDone={() => setTab("chat")} targetSection={currentSection} />
)}
{tab === "account" && <AccountView userInfo={cloudUserInfo} onDone={() => switchTab("chat")} />}
{tab === "account" && (
<AccountView
userInfo={cloudUserInfo}
isAuthenticated={cloudIsAuthenticated}
onDone={() => switchTab("chat")}
/>
)}
<ChatView
ref={chatViewRef}
isHidden={tab !== "chat"}

View file

@ -7,10 +7,11 @@ import { vscode } from "@src/utils/vscode"
type AccountViewProps = {
userInfo: CloudUserInfo | null
isAuthenticated: boolean
onDone: () => void
}
export const AccountView = ({ userInfo, onDone }: AccountViewProps) => {
export const AccountView = ({ userInfo, isAuthenticated, onDone }: AccountViewProps) => {
const { t } = useAppTranslation()
const rooLogoUri = (window as any).IMAGES_BASE_URI + "/roo-logo.svg"
@ -23,27 +24,29 @@ export const AccountView = ({ userInfo, onDone }: AccountViewProps) => {
{t("settings:common.done")}
</VSCodeButton>
</div>
{userInfo ? (
{isAuthenticated ? (
<>
<div className="flex flex-col items-center mb-6">
<div className="w-16 h-16 mb-3 rounded-full overflow-hidden">
{userInfo?.picture ? (
<img
src={userInfo.picture}
alt={t("account:profilePicture")}
className="w-full h-full object-cover"
/>
) : (
<div className="w-full h-full flex items-center justify-center bg-vscode-button-background text-vscode-button-foreground text-xl">
{userInfo?.name?.charAt(0) || userInfo?.email?.charAt(0) || "?"}
</div>
)}
{userInfo && (
<div className="flex flex-col items-center mb-6">
<div className="w-16 h-16 mb-3 rounded-full overflow-hidden">
{userInfo?.picture ? (
<img
src={userInfo.picture}
alt={t("account:profilePicture")}
className="w-full h-full object-cover"
/>
) : (
<div className="w-full h-full flex items-center justify-center bg-vscode-button-background text-vscode-button-foreground text-xl">
{userInfo?.name?.charAt(0) || userInfo?.email?.charAt(0) || "?"}
</div>
)}
</div>
<h2 className="text-lg font-medium text-vscode-foreground mb-1">
{userInfo?.name || t("account:unknownUser")}
</h2>
<p className="text-sm text-vscode-descriptionForeground">{userInfo?.email || ""}</p>
</div>
<h2 className="text-lg font-medium text-vscode-foreground mb-1">
{userInfo?.name || t("account:unknownUser")}
</h2>
<p className="text-sm text-vscode-descriptionForeground">{userInfo?.email || ""}</p>
</div>
)}
<div className="flex flex-col gap-2 mt-4">
<VSCodeButton
appearance="secondary"

View file

@ -35,6 +35,7 @@ export interface ExtensionStateContextType extends ExtensionState {
filePaths: string[]
openedTabs: Array<{ label: string; isActive: boolean; path?: string }>
organizationAllowList: OrganizationAllowList
cloudIsAuthenticated: boolean
maxConcurrentFileReads?: number
condensingApiConfigId?: string
setCondensingApiConfigId: (value: string) => void
@ -199,6 +200,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
terminalCompressProgressBar: true, // Default to compress progress bar output
historyPreviewCollapsed: false, // Initialize the new state (default to expanded)
cloudUserInfo: null,
cloudIsAuthenticated: false,
organizationAllowList: ORGANIZATION_ALLOW_ALL,
autoCondenseContext: true,
autoCondenseContextPercent: 100,
@ -317,6 +319,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
writeDelayMs: state.writeDelayMs,
screenshotQuality: state.screenshotQuality,
routerModels: extensionRouterModels,
cloudIsAuthenticated: state.cloudIsAuthenticated ?? false,
setExperimentEnabled: (id, enabled) =>
setState((prevState) => ({ ...prevState, experiments: { ...prevState.experiments, [id]: enabled } })),
setApiConfiguration,

View file

@ -207,6 +207,7 @@ describe("mergeExtensionState", () => {
organizationAllowList: { allowAll: true, providers: {} },
autoCondenseContext: true,
autoCondenseContextPercent: 100,
cloudIsAuthenticated: false,
}
const prevState: ExtensionState = {