1+ import {
2+ createManagementApiSdk ,
3+ type TokenStorage ,
4+ type Tokens ,
5+ } from "@prisma/management-api-sdk" ;
16import {
27 FileTokenStorage ,
38 type StoredAuthWorkspace ,
@@ -8,7 +13,11 @@ import {
813 performLogout ,
914 readAuthState ,
1015} from "../lib/auth/auth-ops" ;
11- import { SERVICE_TOKEN_ENV_VAR } from "../lib/auth/client" ;
16+ import {
17+ CLIENT_ID ,
18+ getApiBaseUrl ,
19+ SERVICE_TOKEN_ENV_VAR ,
20+ } from "../lib/auth/client" ;
1221import {
1322 authRequiredError ,
1423 usageError ,
@@ -217,7 +226,11 @@ async function listRealAuthWorkspaces(
217226 context . runtime . env ,
218227 context . runtime . signal ,
219228 ) ;
220- const localWorkspaces = await storage . listWorkspaces ( ) ;
229+ const localWorkspaces = await hydrateLocalAuthWorkspaces (
230+ context ,
231+ storage ,
232+ await storage . listWorkspaces ( ) ,
233+ ) ;
221234
222235 if ( rawServiceToken !== undefined ) {
223236 const authState = await readAuthState (
@@ -279,6 +292,11 @@ async function useRealAuthWorkspace(
279292 context . runtime . env ,
280293 context . runtime . signal ,
281294 ) ;
295+ await hydrateLocalAuthWorkspaces (
296+ context ,
297+ storage ,
298+ await storage . listWorkspaces ( ) ,
299+ ) ;
282300
283301 try {
284302 const result = await storage . useWorkspace ( workspaceRef ) ;
@@ -316,6 +334,11 @@ async function logoutRealAuthWorkspace(
316334 context . runtime . env ,
317335 context . runtime . signal ,
318336 ) ;
337+ await hydrateLocalAuthWorkspaces (
338+ context ,
339+ storage ,
340+ await storage . listWorkspaces ( ) ,
341+ ) ;
319342
320343 try {
321344 const result = await storage . logoutWorkspace ( workspaceRef ) ;
@@ -399,6 +422,135 @@ async function selectWorkspaceSession(
399422 return selected . id ;
400423}
401424
425+ async function hydrateLocalAuthWorkspaces (
426+ context : CommandContext ,
427+ storage : FileTokenStorage ,
428+ workspaces : StoredAuthWorkspace [ ] ,
429+ ) : Promise < StoredAuthWorkspace [ ] > {
430+ const candidates = workspaces . filter ( needsWorkspaceMetadataHydration ) ;
431+ if ( candidates . length === 0 ) return workspaces ;
432+
433+ const tokensByCredentialWorkspaceId = new Map (
434+ ( await storage . listWorkspaceTokens ( ) ) . map ( ( tokens ) => [
435+ tokens . workspaceId ,
436+ tokens ,
437+ ] ) ,
438+ ) ;
439+ let nextWorkspaces = workspaces ;
440+
441+ for ( const workspace of candidates ) {
442+ const tokens = tokensByCredentialWorkspaceId . get (
443+ workspace . credentialWorkspaceId ,
444+ ) ;
445+ if ( ! tokens ) continue ;
446+
447+ const resolved = await resolveOAuthWorkspaceMetadata ( context , tokens ) ;
448+ if ( ! resolved ) continue ;
449+
450+ await rememberResolvedWorkspaceMetadata ( context , storage , tokens , resolved ) ;
451+ nextWorkspaces = nextWorkspaces . map ( ( candidate ) =>
452+ candidate . credentialWorkspaceId === workspace . credentialWorkspaceId
453+ ? {
454+ ...candidate ,
455+ id : resolved . id ,
456+ name : resolved . name ,
457+ lastSeenAt : new Date ( ) . toISOString ( ) ,
458+ }
459+ : candidate ,
460+ ) ;
461+ }
462+
463+ return nextWorkspaces ;
464+ }
465+
466+ async function rememberResolvedWorkspaceMetadata (
467+ context : CommandContext ,
468+ storage : FileTokenStorage ,
469+ tokens : Tokens ,
470+ resolved : { id : string ; name : string } ,
471+ ) : Promise < void > {
472+ try {
473+ await storage . rememberWorkspace ( tokens . workspaceId , resolved ) ;
474+ } catch {
475+ context . runtime . signal ?. throwIfAborted ( ) ;
476+ }
477+ }
478+
479+ function needsWorkspaceMetadataHydration ( workspace : StoredAuthWorkspace ) {
480+ return (
481+ workspace . id === workspace . credentialWorkspaceId ||
482+ workspace . name === "Unknown workspace" ||
483+ workspace . name === workspace . credentialWorkspaceId
484+ ) ;
485+ }
486+
487+ async function resolveOAuthWorkspaceMetadata (
488+ context : CommandContext ,
489+ tokens : Tokens ,
490+ ) : Promise < { id : string ; name : string } | null > {
491+ const refreshStorage = new FileTokenStorage (
492+ context . runtime . env ,
493+ context . runtime . signal ,
494+ { activateOnSetTokens : false } ,
495+ ) ;
496+ const tokenStorage = createSingleWorkspaceTokenStorage (
497+ refreshStorage ,
498+ tokens ,
499+ ) ;
500+ const sdk = createManagementApiSdk ( {
501+ clientId : CLIENT_ID ,
502+ redirectUri : "http://localhost:0/auth/callback" ,
503+ tokenStorage,
504+ apiBaseUrl : getApiBaseUrl ( context . runtime . env ) ,
505+ } ) ;
506+
507+ try {
508+ const { data } = await sdk . client . GET ( "/v1/workspaces/{id}" , {
509+ params : { path : { id : tokens . workspaceId } } ,
510+ signal : context . runtime . signal ,
511+ } ) ;
512+ const id = stringOrNull ( data ?. data ?. id ) ?? tokens . workspaceId ;
513+ const name = stringOrNull ( data ?. data ?. name ) ?? id ;
514+
515+ if ( id === tokens . workspaceId && name === tokens . workspaceId ) {
516+ return null ;
517+ }
518+
519+ return { id, name } ;
520+ } catch {
521+ context . runtime . signal ?. throwIfAborted ( ) ;
522+ return null ;
523+ }
524+ }
525+
526+ function createSingleWorkspaceTokenStorage (
527+ storage : FileTokenStorage ,
528+ initialTokens : Tokens ,
529+ ) : TokenStorage {
530+ let currentTokens : Tokens | null = initialTokens ;
531+
532+ return {
533+ getTokens : async ( ) => currentTokens ,
534+ setTokens : async ( tokens ) => {
535+ currentTokens = tokens ;
536+ await storage . setTokens ( tokens ) ;
537+ } ,
538+ clearTokens : async ( ) => {
539+ const tokens = currentTokens ;
540+ currentTokens = null ;
541+ if ( tokens ) {
542+ await storage . clearTokensIfCurrent ( tokens ) ;
543+ }
544+ } ,
545+ } ;
546+ }
547+
548+ function stringOrNull ( value : unknown ) : string | null {
549+ return typeof value === "string" && value . trim ( ) . length > 0
550+ ? value . trim ( )
551+ : null ;
552+ }
553+
402554function toAuthWorkspace ( workspace : StoredAuthWorkspace ) : AuthWorkspace {
403555 return {
404556 id : workspace . id ,
0 commit comments