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 (
448+ context ,
449+ tokens ,
450+ ) ;
451+ if ( ! resolved ) continue ;
452+
453+ await rememberResolvedWorkspaceMetadata ( context , storage , tokens , resolved ) ;
454+ nextWorkspaces = nextWorkspaces . map ( ( candidate ) =>
455+ candidate . credentialWorkspaceId === workspace . credentialWorkspaceId
456+ ? {
457+ ...candidate ,
458+ id : resolved . id ,
459+ name : resolved . name ,
460+ lastSeenAt : new Date ( ) . toISOString ( ) ,
461+ }
462+ : candidate ,
463+ ) ;
464+ }
465+
466+ return nextWorkspaces ;
467+ }
468+
469+ async function rememberResolvedWorkspaceMetadata (
470+ context : CommandContext ,
471+ storage : FileTokenStorage ,
472+ tokens : Tokens ,
473+ resolved : { id : string ; name : string } ,
474+ ) : Promise < void > {
475+ try {
476+ await storage . rememberWorkspace ( tokens . workspaceId , resolved ) ;
477+ } catch {
478+ context . runtime . signal ?. throwIfAborted ( ) ;
479+ }
480+ }
481+
482+ function needsWorkspaceMetadataHydration ( workspace : StoredAuthWorkspace ) {
483+ return (
484+ workspace . id === workspace . credentialWorkspaceId ||
485+ workspace . name === "Unknown workspace" ||
486+ workspace . name === workspace . credentialWorkspaceId
487+ ) ;
488+ }
489+
490+ async function resolveOAuthWorkspaceMetadata (
491+ context : CommandContext ,
492+ tokens : Tokens ,
493+ ) : Promise < { id : string ; name : string } | null > {
494+ const refreshStorage = new FileTokenStorage (
495+ context . runtime . env ,
496+ context . runtime . signal ,
497+ { activateOnSetTokens : false } ,
498+ ) ;
499+ const tokenStorage = createSingleWorkspaceTokenStorage ( refreshStorage , tokens ) ;
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