@@ -186,10 +186,21 @@ vi.mock("../lib/request/rate-limit-backoff.js", () => ({
186186 refreshAndUpdateToken : vi . fn ( async ( auth : unknown ) => auth ) ,
187187 createCodexHeaders : vi . fn ( ( ) => new Headers ( ) ) ,
188188 handleErrorResponse : vi . fn ( async ( response : Response ) => ( { response } ) ) ,
189- isDeactivatedWorkspaceError : vi . fn ( ( errorBody : unknown , status ?: number ) =>
190- status === 402 &&
191- ( errorBody as { error ?: { code ?: string } } ) ?. error ?. code === "deactivated_workspace" ,
192- ) ,
189+ isDeactivatedWorkspaceError : vi . fn ( ( errorBody : unknown , status ?: number ) => {
190+ if ( status !== 402 || ! errorBody || typeof errorBody !== "object" ) return false ;
191+ const body = errorBody as {
192+ code ?: unknown ;
193+ detail ?: { code ?: unknown } | undefined ;
194+ error ?: { code ?: unknown ; type ?: unknown } | undefined ;
195+ } ;
196+ const code =
197+ ( typeof body . code === "string" && body . code ) ||
198+ ( typeof body . detail ?. code === "string" && body . detail . code ) ||
199+ ( typeof body . error ?. code === "string" && body . error . code ) ||
200+ ( typeof body . error ?. type === "string" && body . error . type ) ||
201+ undefined ;
202+ return code === "deactivated_workspace" ;
203+ } ) ,
193204 getUnsupportedCodexModelInfo : vi . fn ( ( ) => ( { isUnsupported : false } ) ) ,
194205 resolveUnsupportedCodexFallbackModel : vi . fn ( ( ) => undefined ) ,
195206 shouldFallbackToGpt52OnUnsupportedGpt53 : vi . fn ( ( ) => false ) ,
@@ -3498,6 +3509,56 @@ describe("OpenAIOAuthPlugin persistAccountPool", () => {
34983509 } ) ;
34993510 } ) ;
35003511
3512+ it ( "keeps workspace-deactivated flagged entries out of verify-flagged restore" , async ( ) => {
3513+ const cliModule = await import ( "../lib/cli.js" ) ;
3514+ const storageModule = await import ( "../lib/storage.js" ) ;
3515+ const refreshQueueModule = await import ( "../lib/refresh-queue.js" ) ;
3516+
3517+ mockFlaggedStorage . accounts = [
3518+ {
3519+ refreshToken : "flagged-refresh-dead" ,
3520+ organizationId : "org-dead" ,
3521+ accountId : "workspace-dead" ,
3522+ accountIdSource : "manual" ,
3523+ accountLabel : "Dead Workspace" ,
3524+ email : "dead@example.com" ,
3525+ flaggedAt : Date . now ( ) - 500 ,
3526+ flaggedReason : "workspace-deactivated" ,
3527+ lastError : "deactivated_workspace" ,
3528+ addedAt : Date . now ( ) - 500 ,
3529+ lastUsed : Date . now ( ) - 500 ,
3530+ } ,
3531+ ] ;
3532+
3533+ vi . mocked ( cliModule . promptLoginMode )
3534+ . mockResolvedValueOnce ( { mode : "verify-flagged" } )
3535+ . mockResolvedValueOnce ( { mode : "cancel" } ) ;
3536+
3537+ const mockClient = createMockClient ( ) ;
3538+ const { OpenAIOAuthPlugin } = await import ( "../index.js" ) ;
3539+ const plugin = ( await OpenAIOAuthPlugin ( {
3540+ client : mockClient ,
3541+ } as never ) ) as unknown as PluginType ;
3542+ const autoMethod = plugin . auth . methods [ 0 ] as unknown as {
3543+ authorize : ( inputs ?: Record < string , string > ) => Promise < { instructions : string } > ;
3544+ } ;
3545+
3546+ const authResult = await autoMethod . authorize ( ) ;
3547+ expect ( authResult . instructions ) . toBe ( "Authentication cancelled" ) ;
3548+
3549+ expect ( vi . mocked ( refreshQueueModule . queuedRefresh ) ) . not . toHaveBeenCalled ( ) ;
3550+ expect ( mockStorage . accounts ) . toHaveLength ( 0 ) ;
3551+ expect ( vi . mocked ( storageModule . saveFlaggedAccounts ) ) . toHaveBeenCalledWith ( {
3552+ version : 1 ,
3553+ accounts : expect . arrayContaining ( [
3554+ expect . objectContaining ( {
3555+ accountId : "workspace-dead" ,
3556+ flaggedReason : "workspace-deactivated" ,
3557+ } ) ,
3558+ ] ) ,
3559+ } ) ;
3560+ } ) ;
3561+
35013562 it ( "removes only the token-invalid org-scoped workspace during deep-check cleanup" , async ( ) => {
35023563 const cliModule = await import ( "../lib/cli.js" ) ;
35033564 const refreshQueueModule = await import ( "../lib/refresh-queue.js" ) ;
0 commit comments