@@ -24,6 +24,8 @@ export interface HyperpodCluster {
2424 clusterName : string
2525 clusterArn : string
2626 status : string
27+ eksClusterName ?: string
28+ eksClusterArn ?: string
2729 regionCode : string
2830}
2931
@@ -34,24 +36,30 @@ export interface WorkspaceConnectionResult {
3436 sessionId : string
3537}
3638
37- /** Refresh the token 1 minute before it expires . */
39+ /** Buffer time (ms) before token expiry to trigger a proactive refresh, avoiding mid-request expirations . */
3840const tokenRefreshBufferMs = 60_000
3941
42+ export interface EksClusterInfo {
43+ name ?: string
44+ endpoint ?: string
45+ certificateAuthority ?: { data ?: string }
46+ }
47+
4048export class KubectlClient {
4149 private kubeConfig : k8s . KubeConfig
42- private k8sApi ! : k8s . CustomObjectsApi
50+ private k8sApi : k8s . CustomObjectsApi | undefined
4351 private tokenExpiry : number = 0
4452
45- private constructor (
46- private readonly eksCluster : any ,
53+ protected constructor (
54+ private readonly eksCluster : EksClusterInfo ,
4755 private readonly hyperpodCluster : HyperpodCluster ,
4856 private readonly credentials : AwsCredentialIdentity | Provider < AwsCredentialIdentity >
4957 ) {
5058 this . kubeConfig = new k8s . KubeConfig ( )
5159 }
5260
53- static async create (
54- eksCluster : any ,
61+ static async createForCluster (
62+ eksCluster : EksClusterInfo ,
5563 hyperpodCluster : HyperpodCluster ,
5664 credentials : AwsCredentialIdentity | Provider < AwsCredentialIdentity >
5765 ) : Promise < KubectlClient > {
@@ -60,10 +68,17 @@ export class KubectlClient {
6068 return client
6169 }
6270
63- getEksCluster ( ) : any {
71+ getEksCluster ( ) : EksClusterInfo {
6472 return this . eksCluster
6573 }
6674
75+ protected getApi ( ) : k8s . CustomObjectsApi {
76+ if ( ! this . k8sApi ) {
77+ throw new Error ( '[Hyperpod] KubectlClient not initialized — call createForCluster()' )
78+ }
79+ return this . k8sApi
80+ }
81+
6782 async createWorkspaceConnection ( devSpace : HyperpodDevSpace ) : Promise < WorkspaceConnectionResult > {
6883 try {
6984 await this . ensureValidToken ( )
@@ -84,7 +99,7 @@ export class KubectlClient {
8499 } ,
85100 }
86101
87- const response = await this . k8sApi . createNamespacedCustomObject (
102+ const response = await this . getApi ( ) . createNamespacedCustomObject (
88103 group ,
89104 version ,
90105 devSpace . namespace ,
@@ -96,6 +111,8 @@ export class KubectlClient {
96111 const status = body . status
97112 const presignedUrl = status ?. workspaceConnectionUrl
98113 const connectionType = status ?. workspaceConnectionType
114+ // tokenValue and sessionId may not be present in all API responses.
115+ // When empty, the existing connection flow in model.ts falls back to parsing these from the presigned URL.
99116 const token = status ?. tokenValue ?? ''
100117 const sessionId = status ?. sessionId ?? ''
101118
@@ -107,7 +124,7 @@ export class KubectlClient {
107124 }
108125 }
109126
110- private async initKubeConfig ( ) : Promise < void > {
127+ protected async initKubeConfig ( ) : Promise < void > {
111128 if ( ! this . eksCluster . name || ! this . eksCluster . endpoint ) {
112129 return
113130 }
@@ -147,13 +164,13 @@ export class KubectlClient {
147164 this . k8sApi = this . kubeConfig . makeApiClient ( k8s . CustomObjectsApi )
148165 }
149166
150- private async ensureValidToken ( ) : Promise < void > {
167+ protected async ensureValidToken ( ) : Promise < void > {
151168 if ( Date . now ( ) >= this . tokenExpiry - tokenRefreshBufferMs ) {
152169 await this . refreshToken ( )
153170 }
154171 }
155172
156- private async refreshToken ( ) : Promise < void > {
173+ protected async refreshToken ( ) : Promise < void > {
157174 if ( ! this . eksCluster . name ) {
158175 return
159176 }
0 commit comments