33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6- import { createReadStream } from 'fs' ;
6+ import { createReadStream , existsSync , writeFileSync } from 'fs' ;
77import { readFile } from 'fs/promises' ;
88import { Promises } from 'vs/base/node/pfs' ;
99import * as path from 'path' ;
@@ -102,6 +102,7 @@ export class WebClientServer {
102102 private readonly _callbackRoute : string ;
103103 private readonly _webExtensionRoute : string ;
104104 private readonly _idleRoute : string ;
105+ private readonly _envMetadata : string ;
105106
106107 constructor (
107108 private readonly _connectionToken : ServerConnectionToken ,
@@ -118,6 +119,7 @@ export class WebClientServer {
118119 this . _callbackRoute = `${ serverRootPath } /callback` ;
119120 this . _webExtensionRoute = `${ serverRootPath } /web-extension-resource` ;
120121 this . _idleRoute = '/api/idle' ;
122+ this . _envMetadata = '/api/env' ;
121123 }
122124
123125 /**
@@ -146,6 +148,9 @@ export class WebClientServer {
146148 // extension resource support
147149 return this . _handleWebExtensionResource ( req , res , parsedUrl ) ;
148150 }
151+ if ( pathname === this . _envMetadata ) {
152+ return this . _handleEnvMetadata ( req , res ) ;
153+ }
149154
150155 return serveError ( req , res , 404 , 'Not found.' ) ;
151156 } catch ( error ) {
@@ -459,12 +464,20 @@ export class WebClientServer {
459464 }
460465
461466 /**
462- * Handles API requests to retrieve the last activity timestamp.
463- */
467+ * Handles API requests to retrieve the last activity timestamp.
468+ */
464469 private async _handleIdle ( req : http . IncomingMessage , res : http . ServerResponse ) : Promise < void > {
465470 try {
466471 const tmpDirectory = '/tmp/'
467472 const idleFilePath = path . join ( tmpDirectory , '.sagemaker-last-active-timestamp' ) ;
473+
474+ // If idle shutdown file does not exist, this indicates the app UI may never been opened
475+ // Create the initial metadata file
476+ if ( ! existsSync ( idleFilePath ) ) {
477+ const timestamp = new Date ( ) . toISOString ( ) ;
478+ writeFileSync ( idleFilePath , timestamp ) ;
479+ }
480+
468481 const data = await readFile ( idleFilePath , 'utf8' ) ;
469482
470483 res . statusCode = 200 ;
@@ -474,6 +487,26 @@ export class WebClientServer {
474487 serveError ( req , res , 500 , error . message )
475488 }
476489 }
490+
491+ /**
492+ * Handles API requests to retrieve the /opt/ml/metadata/resource-metadata.json file.
493+ */
494+ private async _handleEnvMetadata ( req : http . IncomingMessage , res : http . ServerResponse ) : Promise < void > {
495+ try {
496+ const envMetadataDirectory = '/opt/ml/metadata/' ;
497+ const envMetadataFilePath = path . join ( envMetadataDirectory , 'resource-metadata.json' ) ;
498+ if ( existsSync ( envMetadataFilePath ) ) {
499+ const envMetadata = await readFile ( envMetadataFilePath , 'utf8' ) ;
500+ res . statusCode = 200 ;
501+ res . setHeader ( 'Content-Type' , 'application/json' ) ;
502+ res . end ( envMetadata ) ;
503+ } else {
504+ serveError ( req , res , 500 , 'No metadata file at ' + envMetadataFilePath ) ;
505+ }
506+ } catch ( error ) {
507+ serveError ( req , res , 500 , error . message ) ;
508+ }
509+ }
477510}
478511
479512/**
0 commit comments