@@ -111,6 +111,11 @@ export type PairedDeviceMetadataPatch = Pick<
111111 | "lastSeenReason"
112112> ;
113113
114+ export type DevicePairingAccessMetadata = Pick <
115+ PairedDevice ,
116+ "displayName" | "remoteIp" | "lastSeenAtMs" | "lastSeenReason"
117+ > ;
118+
114119export type DevicePairingList = {
115120 pending : DevicePairingPendingRequest [ ] ;
116121 paired : PairedDevice [ ] ;
@@ -458,6 +463,36 @@ function buildDeviceAuthToken(params: {
458463 } ;
459464}
460465
466+ function buildApprovedPairedDevice ( params : {
467+ pending : DevicePairingPendingRequest ;
468+ existing : PairedDevice | undefined ;
469+ roles : string [ ] | undefined ;
470+ approvedScopes : string [ ] | undefined ;
471+ tokens : Record < string , DeviceAuthToken > ;
472+ now : number ;
473+ accessMetadata ?: DevicePairingAccessMetadata ;
474+ } ) : PairedDevice {
475+ return {
476+ deviceId : params . pending . deviceId ,
477+ publicKey : params . pending . publicKey ,
478+ displayName : params . accessMetadata ?. displayName ?? params . pending . displayName ,
479+ platform : params . pending . platform ,
480+ deviceFamily : params . pending . deviceFamily ,
481+ clientId : params . pending . clientId ,
482+ clientMode : params . pending . clientMode ,
483+ role : params . pending . role ,
484+ roles : params . roles ,
485+ scopes : params . approvedScopes ,
486+ approvedScopes : params . approvedScopes ,
487+ remoteIp : params . accessMetadata ?. remoteIp ?? params . pending . remoteIp ,
488+ tokens : params . tokens ,
489+ createdAtMs : params . existing ?. createdAtMs ?? params . now ,
490+ approvedAtMs : params . now ,
491+ lastSeenAtMs : params . accessMetadata ?. lastSeenAtMs ?? params . existing ?. lastSeenAtMs ,
492+ lastSeenReason : params . accessMetadata ?. lastSeenReason ?? params . existing ?. lastSeenReason ,
493+ } ;
494+ }
495+
461496function resolveRoleScopedDeviceTokenScopes ( role : string , scopes : string [ ] | undefined ) : string [ ] {
462497 const normalized = normalizeDeviceAuthScopes ( scopes ) ;
463498 if ( role === "operator" ) {
@@ -620,12 +655,14 @@ export async function approveDevicePairing(
620655) : Promise < ApproveDevicePairingResult > ;
621656export async function approveDevicePairing (
622657 requestId : string ,
623- options : { callerScopes ?: readonly string [ ] } ,
658+ options : { callerScopes ?: readonly string [ ] ; accessMetadata ?: DevicePairingAccessMetadata } ,
624659 baseDir ?: string ,
625660) : Promise < ApproveDevicePairingResult > ;
626661export async function approveDevicePairing (
627662 requestId : string ,
628- optionsOrBaseDir ?: { callerScopes ?: readonly string [ ] } | string ,
663+ optionsOrBaseDir ?:
664+ | { callerScopes ?: readonly string [ ] ; accessMetadata ?: DevicePairingAccessMetadata }
665+ | string ,
629666 maybeBaseDir ?: string ,
630667) : Promise < ApproveDevicePairingResult > {
631668 const options =
@@ -707,23 +744,15 @@ export async function approveDevicePairing(
707744 lastUsedAtMs : existingToken ?. lastUsedAtMs ,
708745 } ;
709746 }
710- const device : PairedDevice = {
711- deviceId : pending . deviceId ,
712- publicKey : pending . publicKey ,
713- displayName : pending . displayName ,
714- platform : pending . platform ,
715- deviceFamily : pending . deviceFamily ,
716- clientId : pending . clientId ,
717- clientMode : pending . clientMode ,
718- role : pending . role ,
747+ const device = buildApprovedPairedDevice ( {
748+ pending,
749+ existing,
719750 roles,
720- scopes : approvedScopes ,
721751 approvedScopes,
722- remoteIp : pending . remoteIp ,
723752 tokens,
724- createdAtMs : existing ?. createdAtMs ?? now ,
725- approvedAtMs : now ,
726- } ;
753+ now,
754+ accessMetadata : options ?. accessMetadata ,
755+ } ) ;
727756 delete state . pendingById [ requestId ] ;
728757 state . pairedByDeviceId [ device . deviceId ] = device ;
729758 await persistState ( state , baseDir , "both" ) ;
@@ -735,7 +764,24 @@ export async function approveBootstrapDevicePairing(
735764 requestId : string ,
736765 bootstrapProfile : DeviceBootstrapProfile ,
737766 baseDir ?: string ,
767+ ) : Promise < ApproveDevicePairingResult > ;
768+ export async function approveBootstrapDevicePairing (
769+ requestId : string ,
770+ bootstrapProfile : DeviceBootstrapProfile ,
771+ options : { accessMetadata ?: DevicePairingAccessMetadata } ,
772+ baseDir ?: string ,
773+ ) : Promise < ApproveDevicePairingResult > ;
774+ export async function approveBootstrapDevicePairing (
775+ requestId : string ,
776+ bootstrapProfile : DeviceBootstrapProfile ,
777+ optionsOrBaseDir ?: { accessMetadata ?: DevicePairingAccessMetadata } | string ,
778+ maybeBaseDir ?: string ,
738779) : Promise < ApproveDevicePairingResult > {
780+ const options =
781+ typeof optionsOrBaseDir === "string" || optionsOrBaseDir === undefined
782+ ? undefined
783+ : optionsOrBaseDir ;
784+ const baseDir = typeof optionsOrBaseDir === "string" ? optionsOrBaseDir : maybeBaseDir ;
739785 const approvedRoles = mergeRoles ( bootstrapProfile . roles ) ?? [ ] ;
740786 const approvedScopes = resolveBootstrapProfileScopesForRoles (
741787 approvedRoles ,
@@ -796,23 +842,15 @@ export async function approveBootstrapDevicePairing(
796842 } ) ;
797843 }
798844
799- const device : PairedDevice = {
800- deviceId : pending . deviceId ,
801- publicKey : pending . publicKey ,
802- displayName : pending . displayName ,
803- platform : pending . platform ,
804- deviceFamily : pending . deviceFamily ,
805- clientId : pending . clientId ,
806- clientMode : pending . clientMode ,
807- role : pending . role ,
845+ const device = buildApprovedPairedDevice ( {
846+ pending,
847+ existing,
808848 roles,
809- scopes : nextApprovedScopes ,
810849 approvedScopes : nextApprovedScopes ,
811- remoteIp : pending . remoteIp ,
812850 tokens,
813- createdAtMs : existing ?. createdAtMs ?? now ,
814- approvedAtMs : now ,
815- } ;
851+ now,
852+ accessMetadata : options ?. accessMetadata ,
853+ } ) ;
816854 delete state . pendingById [ requestId ] ;
817855 state . pairedByDeviceId [ device . deviceId ] = device ;
818856 await persistState ( state , baseDir , "both" ) ;
0 commit comments