diff --git a/patches/display-language.patch b/patches/display-language.patch
index 95c9a2197..2eea9fe80 100644
--- a/patches/display-language.patch
+++ b/patches/display-language.patch
@@ -1,139 +1,3 @@
-Index: sagemaker-code-editor/vscode/src/vs/base/common/platform.ts
-===================================================================
---- sagemaker-code-editor.orig/vscode/src/vs/base/common/platform.ts
-+++ sagemaker-code-editor/vscode/src/vs/base/common/platform.ts
-@@ -2,9 +2,6 @@
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
--
--import * as nls from '../../nls.js';
--
- export const LANGUAGE_DEFAULT = 'en';
-
- let _isWindows = false;
-@@ -23,6 +20,13 @@
- let _translationsConfigFile: string | undefined = undefined;
- let _userAgent: string | undefined = undefined;
-
-+interface NLSConfig {
-+ locale: string;
-+ osLocale: string;
-+ availableLanguages: { [key: string]: string };
-+ _translationsConfigFile: string;
-+}
-+
- export interface IProcessEnvironment {
- [key: string]: string | undefined;
- }
-@@ -83,15 +80,16 @@ if (typeof nodeProcess === 'obj
- const rawNlsConfig = nodeProcess.env['VSCODE_NLS_CONFIG'];
- if (rawNlsConfig) {
- try {
-- const nlsConfig: nls.INLSConfiguration = JSON.parse(rawNlsConfig);
-+ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
-+ const resolved = nlsConfig.availableLanguages['*'];
-- _locale = nlsConfig.userLocale;
-+ _locale = nlsConfig.locale;
- _platformLocale = nlsConfig.osLocale;
-- _language = nlsConfig.resolvedLanguage || LANGUAGE_DEFAULT;
-+ _language = resolved ? resolved : LANGUAGE_DEFAULT;
-- _translationsConfigFile = nlsConfig.languagePack?.translationsConfigFile;
-+ _translationsConfigFile = nlsConfig._translationsConfigFile;
- } catch (e) {
- }
- }
- _isNative = true;
- }
-
- // Web environment
-@@ -104,8 +102,20 @@ else if (typeof navigator === 'object' &
- _isMobile = _userAgent?.indexOf('Mobi') >= 0;
- _isWeb = true;
-- _language = nls.getNLSLanguage() || LANGUAGE_DEFAULT;
-- _locale = navigator.language.toLowerCase();
-- _platformLocale = _locale;
-+ _locale = LANGUAGE_DEFAULT;
-+ _language = _locale;
-+ _platformLocale = navigator.language;
-+ const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration');
-+ const rawNlsConfig = el && el.getAttribute('data-settings');
-+ if (rawNlsConfig) {
-+ try {
-+ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
-+ const resolved = nlsConfig.availableLanguages['*'];
-+ _locale = nlsConfig.locale;
-+ _platformLocale = nlsConfig.osLocale;
-+ _language = resolved ? resolved : LANGUAGE_DEFAULT;
-+ _translationsConfigFile = nlsConfig._translationsConfigFile;
-+ } catch (error) { /* Oh well. */ }
-+ }
- }
-
- // Unknown environment
-Index: sagemaker-code-editor/vscode/src/vs/code/browser/workbench/workbench.html
-===================================================================
---- sagemaker-code-editor.orig/vscode/src/vs/code/browser/workbench/workbench.html
-+++ sagemaker-code-editor/vscode/src/vs/code/browser/workbench/workbench.html
-@@ -19,6 +19,9 @@
-
-
-
-+
-+
-+
-
-
-
-@@ -37,6 +40,47 @@
- const baseUrl = new URL('{{WORKBENCH_WEB_BASE_URL}}', window.location.origin).toString();
- globalThis._VSCODE_FILE_ROOT = baseUrl + '/out/';
-+
-+ // Set up nls if the user is not using the default language (English)
-+ const nlsConfig = {};
-+ // Normalize locale to lowercase because translationServiceUrl is case-sensitive.
-+ // ref: https://github.com/microsoft/vscode/issues/187795
-+ const locale = localStorage.getItem('vscode.nls.locale') || navigator.language.toLowerCase();
-+ try {
-+ nlsConfig['vs/nls'] = JSON.parse(document.getElementById("vscode-remote-nls-configuration").getAttribute("data-settings"))
-+ if (nlsConfig['vs/nls']._resolvedLanguagePackCoreLocation) {
-+ const bundles = Object.create(null)
-+ nlsConfig['vs/nls'].loadBundle = (bundle, _language, cb) => {
-+ const result = bundles[bundle]
-+ if (result) {
-+ return cb(undefined, result)
-+ }
-+ const path = nlsConfig['vs/nls']._resolvedLanguagePackCoreLocation + "/" + bundle.replace(/\//g, "!") + ".nls.json"
-+ fetch(`{{WORKBENCH_WEB_BASE_URL}}/../vscode-remote-resource?path=${encodeURIComponent(path)}`)
-+ .then((response) => response.json())
-+ .then((json) => {
-+ bundles[bundle] = json
-+ cb(undefined, json)
-+ })
-+ .catch(cb)
-+ }
-+ }
-+ } catch (error) { /* Probably fine. */ }
-+
-+ require.config({
-+ baseUrl: `${baseUrl}/out`,
-+ recordStats: true,
-+ trustedTypesPolicy: window.trustedTypes?.createPolicy('amdLoader', {
-+ createScriptURL(value) {
-+ if(value.startsWith(window.location.origin)) {
-+ return value;
-+ }
-+ throw new Error(`Invalid script url: ${value}`)
-+ }
-+ }),
-+ paths: self.webPackagePaths,
-+ ...nlsConfig
-+ });
-
-
-
Index: sagemaker-code-editor/vscode/src/vs/platform/environment/common/environmentService.ts
===================================================================
--- sagemaker-code-editor.orig/vscode/src/vs/platform/environment/common/environmentService.ts
@@ -186,6 +50,139 @@ Index: sagemaker-code-editor/vscode/src/vs/platform/languagePacks/browser/langua
+ return this.languagePackService.getInstalledLanguages()
}
}
+Index: sagemaker-code-editor/vscode/src/vs/server/node/remoteLanguagePacks.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/server/node/remoteLanguagePacks.ts
++++ sagemaker-code-editor/vscode/src/vs/server/node/remoteLanguagePacks.ts
+@@ -3,37 +3,111 @@
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
++import * as path from 'path';
++import { promises as fs } from 'fs';
+ import { FileAccess } from '../../base/common/network.js';
+ import { join } from '../../base/common/path.js';
+ import type { INLSConfiguration } from '../../nls.js';
+ import { resolveNLSConfiguration } from '../../base/node/nls.js';
+-import { Promises } from '../../base/node/pfs.js';
+-import product from '../../platform/product/common/product.js';
+
+ const nlsMetadataPath = join(FileAccess.asFileUri('').fsPath);
+-const defaultMessagesFile = join(nlsMetadataPath, 'nls.messages.json');
+ const nlsConfigurationCache = new Map>();
+
+ export async function getNLSConfiguration(language: string, userDataPath: string): Promise {
+- if (!product.commit || !(await Promises.exists(defaultMessagesFile))) {
+- return {
+- userLocale: 'en',
+- osLocale: 'en',
+- resolvedLanguage: 'en',
+- defaultMessagesFile,
+-
+- // NLS: below 2 are a relic from old times only used by vscode-nls and deprecated
+- locale: 'en',
+- availableLanguages: {}
+- };
+- }
+-
+ const cacheKey = `${language}||${userDataPath}`;
+ let result = nlsConfigurationCache.get(cacheKey);
+ if (!result) {
+- result = resolveNLSConfiguration({ userLocale: language, osLocale: language, commit: product.commit, userDataPath, nlsMetadataPath });
++ // passing a dummy commit which is required to resolve language packs
++ result = resolveNLSConfiguration({ userLocale: language, osLocale: language, commit: 'dummy_commit', userDataPath, nlsMetadataPath });
+ nlsConfigurationCache.set(cacheKey, result);
++ // If the language pack does not yet exist, it defaults to English, which is
++ // then cached and you have to restart even if you then install the pack.
++ result.then((r) => {
++ if (!language.startsWith('en') && r.resolvedLanguage.startsWith('en')) {
++ nlsConfigurationCache.delete(cacheKey);
++ }
++ })
+ }
+
+ return result;
+ }
++
++/**
++ * Copied from from src/main.js.
++ */
++export const getLocaleFromConfig = async (argvResource: string): Promise => {
++ try {
++ const content = stripComments(await fs.readFile(argvResource, 'utf8'));
++ return JSON.parse(content).locale;
++ } catch (error) {
++ if (error.code !== "ENOENT") {
++ console.warn(error)
++ }
++ return 'en';
++ }
++};
++
++/**
++ * Copied from from src/main.js.
++ */
++const stripComments = (content: string): string => {
++ const regexp = /('(?:[^\\']*(?:\\.)?)*')|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
++
++ return content.replace(regexp, (match, _m1, _m2, m3, m4) => {
++ // Only one of m1, m2, m3, m4 matches
++ if (m3) {
++ // A block comment. Replace with nothing
++ return '';
++ } else if (m4) {
++ // A line comment. If it ends in \r?\n then keep it.
++ const length_1 = m4.length;
++ if (length_1 > 2 && m4[length_1 - 1] === '\n') {
++ return m4[length_1 - 2] === '\r' ? '\r\n' : '\n';
++ }
++ else {
++ return '';
++ }
++ } else {
++ // We match a string
++ return match;
++ }
++ });
++};
++
++/**
++ * Generate translations then return a path to a JavaScript file that sets the
++ * translations into global variables. This file is loaded by the browser to
++ * set global variables that the loader uses when looking for translations.
++ *
++ * Normally, VS Code pulls these files from a CDN but we want them to be local.
++ */
++export async function getBrowserNLSConfiguration(locale: string, userDataPath: string): Promise {
++ if (locale.startsWith('en')) {
++ return ''; // Use fallback translations.
++ }
++
++ const nlsConfig = await getNLSConfiguration(locale, userDataPath);
++ const messagesFile = nlsConfig?.languagePack?.messagesFile;
++ const resolvedLanguage = nlsConfig?.resolvedLanguage;
++ if (!messagesFile || !resolvedLanguage) {
++ return ''; // Use fallback translations.
++ }
++
++ const nlsFile = path.join(path.dirname(messagesFile), "nls.messages.js");
++ try {
++ await fs.stat(nlsFile);
++ return nlsFile; // We already generated the file.
++ } catch (error) {
++ // ENOENT is fine, that just means we need to generate the file.
++ if (error.code !== 'ENOENT') {
++ throw error;
++ }
++ }
++
++ const messages = (await fs.readFile(messagesFile)).toString();
++ const content = `globalThis._VSCODE_NLS_MESSAGES=${messages};
++globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(resolvedLanguage)};`
++ await fs.writeFile(nlsFile, content, "utf-8");
++
++ return nlsFile;
++}
Index: sagemaker-code-editor/vscode/src/vs/server/node/serverEnvironmentService.ts
===================================================================
@@ -214,7 +211,7 @@ Index: sagemaker-code-editor/vscode/src/vs/server/node/serverServices.ts
===================================================================
--- sagemaker-code-editor.orig/vscode/src/vs/server/node/serverServices.ts
+++ sagemaker-code-editor/vscode/src/vs/server/node/serverServices.ts
-@@ -11,7 +11,7 @@ import * as path from '..
+@@ -12,7 +12,7 @@ import * as path from '..
import { IURITransformer } from '../../base/common/uriIpc.js';
import { getMachineId, getSqmMachineId, getdevDeviceId } from '../../base/node/id.js';
import { Promises } from '../../base/node/pfs.js';
@@ -223,7 +220,7 @@ Index: sagemaker-code-editor/vscode/src/vs/server/node/serverServices.ts
import { ProtocolConstants } from '../../base/parts/ipc/common/ipc.net.js';
import { IConfigurationService } from '../../platform/configuration/common/configuration.js';
import { ConfigurationService } from '../../platform/configuration/common/configurationService.js';
-@@ -225,6 +225,9 @@ export async function setupServerService
+@@ -255,6 +255,9 @@ export async function setupServerService
const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
socketServer.registerChannel('extensions', channel);
@@ -238,16 +235,149 @@ Index: sagemaker-code-editor/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- sagemaker-code-editor.orig/vscode/src/vs/server/node/webClientServer.ts
+++ sagemaker-code-editor/vscode/src/vs/server/node/webClientServer.ts
-@@ -401,7 +405,7 @@ export class WebClientServer {
- `frame-src 'self' https://*.vscode-cdn.net data:;`,
- 'worker-src \'self\' data: blob:;',
- 'style-src \'self\' \'unsafe-inline\';',
-- 'connect-src \'self\' ws: wss: https://main.vscode-cdn.net http://localhost:* https://localhost:* https://login.microsoftonline.com/ https://update.code.visualstudio.com https://*.vscode-unpkg.net/ https://default.exp-tas.com/vscode/ab https://vscode-sync.trafficmanager.net https://vscode-sync-insiders.trafficmanager.net https://*.gallerycdn.vsassets.io https://marketplace.visualstudio.com https://az764295.vo.msecnd.net https://code.visualstudio.com https://*.gallery.vsassets.io https://*.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com https://*.servicebus.windows.net/ https://vscode.blob.core.windows.net https://vscode.search.windows.net https://vsmarketplacebadges.dev https://vscode.download.prss.microsoft.com https://download.visualstudio.microsoft.com https://*.vscode-unpkg.net https://open-vsx.org;',
-+ 'connect-src \'self\' ws: wss: https://main.vscode-cdn.net http://localhost:* https://localhost:* https://login.microsoftonline.com/ https://update.code.visualstudio.com https://*.vscode-unpkg.net/ https://default.exp-tas.com/vscode/ab https://vscode-sync.trafficmanager.net https://vscode-sync-insiders.trafficmanager.net https://*.gallerycdn.vsassets.io https://marketplace.visualstudio.com https://openvsxorg.blob.core.windows.net https://az764295.vo.msecnd.net https://code.visualstudio.com https://*.gallery.vsassets.io https://*.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com https://*.servicebus.windows.net/ https://vscode.blob.core.windows.net https://vscode.search.windows.net https://vsmarketplacebadges.dev https://vscode.download.prss.microsoft.com https://download.visualstudio.microsoft.com https://*.vscode-unpkg.net https://open-vsx.org;',
- 'font-src \'self\' blob:;',
- 'manifest-src \'self\';'
- ].join(' ');
+@@ -25,6 +25,7 @@ import { URI } from '..
+ import { streamToBuffer } from '../../base/common/buffer.js';
+ import { IProductConfiguration } from '../../base/common/product.js';
+ import { isString, Mutable } from '../../base/common/types.js';
++import { getLocaleFromConfig, getBrowserNLSConfiguration } from '../../server/node/remoteLanguagePacks.js';
+ import { CharCode } from '../../base/common/charCode.js';
+ import { IExtensionManifest } from '../../platform/extensions/common/extensions.js';
+ import { ICSSDevelopmentService } from '../../platform/cssDev/node/cssDevService.js';
+@@ -245,7 +246,10 @@ export class WebClientServer {
+ };
+
+ // Prefix routes with basePath for clients
+- const basePath = getFirstHeader('x-forwarded-prefix') || this._basePath;
++ const proxyPath = this._environmentService.args["base-path"] || "/";
++ const base = relativeRoot(proxyPath);
++ const vscodeBase = relativePath(proxyPath);
++ const basePath = vscodeBase || getFirstHeader("x-forwarded-prefix") || this._basePath;
+
+ const queryConnectionToken = parsedUrl.query[connectionTokenQueryName];
+ if (typeof queryConnectionToken === 'string') {
+@@ -287,7 +291,7 @@ export class WebClientServer {
+ let remoteAuthority = (
+ useTestResolver
+ ? 'test+test'
+- : (getFirstHeader('x-original-host') || getFirstHeader('x-forwarded-host') || req.headers.host)
++ : (getFirstHeader('x-original-host') || getFirstHeader('x-forwarded-host') || req.headers.host || window.location.host)
+ );
+ if (!remoteAuthority) {
+ return serveError(req, res, 400, `Bad request.`);
+@@ -333,6 +337,7 @@ export class WebClientServer {
+ } : undefined;
+
+ const productConfiguration: Partial> = {
++ rootEndpoint: base,
+ embedderIdentifier: 'server-distro',
+ extensionsGallery: this._webExtensionResourceUrlTemplate && this._productService.extensionsGallery ? {
+ ...this._productService.extensionsGallery,
+@@ -364,7 +369,7 @@ export class WebClientServer {
+ const workbenchWebConfiguration = {
+ remoteAuthority,
+- serverBasePath: basePath,
++ serverBasePath: this._basePath,
+ webviewEndpoint: staticRoute + '/out/vs/workbench/contrib/webview/browser/pre',
+ userDataPath: this._environmentService.userDataPath,
+ _wrapWebWorkerExtHostInIframe,
+@@ -385,22 +390,32 @@ export class WebClientServer {
+ };
+
+ const cookies = cookie.parse(req.headers.cookie || '');
+- const locale = cookies['vscode.nls.locale'] || req.headers['accept-language']?.split(',')[0]?.toLowerCase() || 'en';
++ const locale = this._environmentService.args.locale || await getLocaleFromConfig(this._environmentService.argvResource.fsPath) || cookies['vscode.nls.locale'] || req.headers['accept-language']?.split(',')[0]?.toLowerCase() || 'en';
+ let WORKBENCH_NLS_BASE_URL: string | undefined;
+ let WORKBENCH_NLS_URL: string;
+ if (!locale.startsWith('en') && this._productService.nlsCoreBaseUrl) {
+ WORKBENCH_NLS_BASE_URL = this._productService.nlsCoreBaseUrl;
+ WORKBENCH_NLS_URL = `${WORKBENCH_NLS_BASE_URL}${this._productService.commit}/${this._productService.version}/${locale}/nls.messages.js`;
+ } else {
+- WORKBENCH_NLS_URL = ''; // fallback will apply
++ try {
++ const nlsFile = await getBrowserNLSConfiguration(locale, this._environmentService.userDataPath);
++ WORKBENCH_NLS_URL = nlsFile
++ ? `${vscodeBase}/vscode-remote-resource?path=${encodeURIComponent(nlsFile)}`
++ : '';
++ } catch (error) {
++ console.error("Failed to generate translations", error);
++ WORKBENCH_NLS_URL = '';
++ }
+ }
+
+ const values: { [key: string]: string } = {
+ WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
+ WORKBENCH_AUTH_SESSION: authSessionInfo ? asJSON(authSessionInfo) : '',
+ WORKBENCH_WEB_BASE_URL: staticRoute,
+ WORKBENCH_NLS_URL,
+- WORKBENCH_NLS_FALLBACK_URL: `${staticRoute}/out/nls.messages.js`
++ WORKBENCH_NLS_FALLBACK_URL: `${staticRoute}/out/nls.messages.js`,
++ BASE: base,
++ VS_BASE: vscodeBase
+ };
+
+ // DEV ---------------------------------------------------------------------------------------
+@@ -618,3 +633,60 @@ export class WebClientServer {
+ serveError(req, res, 500, error.message);
+ }
+ }
++
++
++/**
++ * Remove extra slashes in a URL.
++ *
++ * This is meant to fill the job of `path.join` so you can concatenate paths and
++ * then normalize out any extra slashes.
++ *
++ * If you are using `path.join` you do not need this but note that `path` is for
++ * file system paths, not URLs.
++ */
++export const normalizeUrlPath = (url: string, keepTrailing = false): string => {
++ return url.replace(/\/\/+/g, "/").replace(/\/+$/, keepTrailing ? "/" : "")
++}
++
++/**
++ * Get the relative path that will get us to the root of the page. For each
++ * slash we need to go up a directory. Will not have a trailing slash.
++ *
++ * For example:
++ *
++ * / => .
++ * /foo => .
++ * /foo/ => ./..
++ * /foo/bar => ./..
++ * /foo/bar/ => ./../..
++ *
++ * All paths must be relative in order to work behind a reverse proxy since we
++ * we do not know the base path. Anything that needs to be absolute (for
++ * example cookies) must get the base path from the frontend.
++ *
++ * All relative paths must be prefixed with the relative root to ensure they
++ * work no matter the depth at which they happen to appear.
++ *
++ * For Express `req.originalUrl` should be used as they remove the base from the
++ * standard `url` property making it impossible to get the true depth.
++ */
++export const relativeRoot = (originalUrl: string): string => {
++ const depth = (originalUrl.split("?", 1)[0].match(/\//g) || []).length
++ return normalizeUrlPath("./" + (depth > 1 ? "../".repeat(depth - 1) : ""))
++}
++
++/**
++ * Get the relative path to the current resource.
++ *
++ * For example:
++ *
++ * / => .
++ * /foo => ./foo
++ * /foo/ => .
++ * /foo/bar => ./bar
++ * /foo/bar/ => .
++ */
++export const relativePath = (originalUrl: string): string => {
++ const parts = originalUrl.split("?", 1)[0].split("/")
++ return normalizeUrlPath("./" + parts[parts.length - 1])
++}
Index: sagemaker-code-editor/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
===================================================================
--- sagemaker-code-editor.orig/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
@@ -340,3 +470,71 @@ Index: sagemaker-code-editor/vscode/src/vs/workbench/services/localization/elect
await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: locale }], true);
return true;
}
+
+Index: sagemaker-code-editor/vscode/build/gulpfile.reh.js
+===================================================================
+--- sagemaker-code-editor.orig/vscode/build/gulpfile.reh.js
++++ sagemaker-code-editor/vscode/build/gulpfile.reh.js
+@@ -59,6 +59,7 @@ const serverResourceIncludes = [
+
+ // NLS
+ 'out-build/nls.messages.json',
++ 'out-build/nls.keys.json', // Required to generate translations.
+
+ // Process monitor
+ 'out-build/vs/base/node/cpuUsage.sh',
+Index: sagemaker-code-editor/vscode/src/vs/workbench/workbench.web.main.internal.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/workbench/workbench.web.main.internal.ts
++++ sagemaker-code-editor/vscode/src/vs/workbench/workbench.web.main.internal.ts
+@@ -53,7 +53,7 @@ import './services/dialogs/browser/fileD
+ import './services/host/browser/browserHostService.js';
+ import './services/lifecycle/browser/lifecycleService.js';
+ import './services/clipboard/browser/clipboardService.js';
+-import './services/localization/browser/localeService.js';
++import './services/localization/electron-sandbox/localeService.js';
+ import './services/path/browser/pathService.js';
+ import './services/themes/browser/browserHostColorSchemeService.js';
+ import './services/encryption/browser/encryptionService.js';
+Index: sagemaker-code-editor/vscode/src/vs/workbench/contrib/localization/common/localizationsActions.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/workbench/contrib/localization/common/localizationsActions.ts
++++ sagemaker-code-editor/vscode/src/vs/workbench/contrib/localization/common/localizationsActions.ts
+@@ -12,6 +12,7 @@ import { ServicesAccessor } from '../../
+ import { ILanguagePackItem, ILanguagePackService } from '../../../../platform/languagePacks/common/languagePacks.js';
+ import { ILocaleService } from '../../../services/localization/common/locale.js';
+ import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js';
++import { language } from '../../../../base/common/platform.js';
+
+ export class ConfigureDisplayLanguageAction extends Action2 {
+ public static readonly ID = 'workbench.action.configureLocale';
+@@ -37,6 +38,16 @@ export class ConfigureDisplayLanguageAct
+
+ const installedLanguages = await languagePackService.getInstalledLanguages();
+
++ // Clean any existing (Current) text and add it back only to the correct locale
++ installedLanguages?.forEach(lang => {
++ if (lang.description) {
++ lang.description = lang.description.replace(/ \(Current\)$/, '');
++ }
++ if (lang.id?.toLowerCase() === language.toLowerCase()) {
++ lang.description = (lang.description || '') + localize('currentDisplayLanguage', " (Current)");
++ }
++ });
++
+ const disposables = new DisposableStore();
+ const qp = disposables.add(quickInputService.createQuickPick({ useSeparators: true }));
+ qp.matchOnDescription = true;
+
+Index: sagemaker-code-editor/vscode/src/vs/base/common/product.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/base/common/product.ts
++++ sagemaker-code-editor/vscode/src/vs/base/common/product.ts
+@@ -56,6 +56,7 @@ export type ExtensionVirtualWorkspaceSup
+ };
+
+ export interface IProductConfiguration {
++ readonly rootEndpoint?: string;
+ readonly version: string;
+ readonly date?: string;
+ readonly quality?: string;
\ No newline at end of file
diff --git a/patches/sagemaker-idle-extension.patch b/patches/sagemaker-idle-extension.patch
index f475f6071..3a1966b34 100644
--- a/patches/sagemaker-idle-extension.patch
+++ b/patches/sagemaker-idle-extension.patch
@@ -137,27 +137,19 @@ Index: sagemaker-code-editor/vscode/extensions/sagemaker-idle-extension/src/exte
===================================================================
--- /dev/null
+++ sagemaker-code-editor/vscode/extensions/sagemaker-idle-extension/src/extension.ts
-@@ -0,0 +1,112 @@
+@@ -0,0 +1,58 @@
+import * as vscode from "vscode";
+import * as fs from "fs";
-+import { join } from "path";
++import * as path from "path";
+
-+let idleFilePath: string
-+let terminalActivityInterval: NodeJS.Timeout | undefined
-+const LOG_PREFIX = "[sagemaker-idle-extension]"
-+const CHECK_INTERVAL = 60000; // 60 seconds interval
++let idleFilePath: string;
+
+export function activate(context: vscode.ExtensionContext) {
+ initializeIdleFilePath();
+ registerEventListeners(context);
-+ startMonitoringTerminalActivity();
+}
+
-+export function deactivate() {
-+ if(terminalActivityInterval) {
-+ clearInterval(terminalActivityInterval)
-+ }
-+}
++export function deactivate() {}
+
+/**
+ * Initializes the file path where the idle timestamp will be stored.
@@ -165,10 +157,10 @@ Index: sagemaker-code-editor/vscode/extensions/sagemaker-idle-extension/src/exte
+ */
+function initializeIdleFilePath() {
+ const tmpDirectory = "/tmp/";
-+ idleFilePath = join(tmpDirectory, ".sagemaker-last-active-timestamp");
++ idleFilePath = path.join(tmpDirectory, ".sagemaker-last-active-timestamp");
+
+ // Set initial lastActivetimestamp
-+ updateLastActivityTimestamp()
++ updateLastActivityTimestamp();
+}
+
+/**
@@ -197,52 +189,6 @@ Index: sagemaker-code-editor/vscode/extensions/sagemaker-idle-extension/src/exte
+}
+
+/**
-+ * Starts monitoring terminal activity by setting an interval to check for activity in the /dev/pts directory.
-+ */
-+const startMonitoringTerminalActivity = () => {
-+ terminalActivityInterval = setInterval(checkTerminalActivity, CHECK_INTERVAL);
-+};
-+
-+
-+/**
-+ * Checks for terminal activity by reading the /dev/pts directory and comparing modification times of the files.
-+ *
-+ * The /dev/pts directory is used in Unix-like operating systems to represent pseudo-terminal (PTY) devices.
-+ * Each active terminal session is assigned a PTY device. These devices are represented as files within the /dev/pts directory.
-+ * When a terminal session has activity, such as when a user inputs commands or output is written to the terminal,
-+ * the modification time (mtime) of the corresponding PTY device file is updated. By monitoring the modification
-+ * times of the files in the /dev/pts directory, we can detect terminal activity.
-+ *
-+ * If activity is detected (i.e., if any PTY device file was modified within the CHECK_INTERVAL), this function
-+ * updates the last activity timestamp.
-+ */
-+const checkTerminalActivity = () => {
-+ fs.readdir("/dev/pts", (err, files) => {
-+ if (err) {
-+ console.error(`${LOG_PREFIX} Error reading /dev/pts directory:`, err);
-+ return;
-+ }
-+
-+ const now = Date.now();
-+ const activityDetected = files.some((file) => {
-+ const filePath = join("/dev/pts", file);
-+ try {
-+ const stats = fs.statSync(filePath);
-+ const mtime = new Date(stats.mtime).getTime();
-+ return now - mtime < CHECK_INTERVAL;
-+ } catch (error) {
-+ console.error(`${LOG_PREFIX} Error reading file stats:`, error);
-+ return false;
-+ }
-+ });
-+
-+ if (activityDetected) {
-+ updateLastActivityTimestamp();
-+ }
-+ });
-+};
-+
-+/**
+ * Updates the last activity timestamp by recording the current timestamp in the idle file and
+ * refreshing the status bar. The timestamp should be in ISO 8601 format and set to the UTC timezone.
+ */
@@ -279,16 +225,62 @@ Index: sagemaker-code-editor/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- sagemaker-code-editor.orig/vscode/src/vs/server/node/webClientServer.ts
+++ sagemaker-code-editor/vscode/src/vs/server/node/webClientServer.ts
-@@ -3,7 +3,7 @@
+@@ -3,7 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { createReadStream, promises } from 'fs';
+import { createReadStream, promises, existsSync, writeFileSync } from 'fs';
++import { readFile } from 'fs/promises'
++import * as path from 'path';
import * as http from 'http';
import * as url from 'url';
import * as cookie from 'cookie';
-@@ -95,6 +95,7 @@ const APP_ROOT = dirname(FileAccess.asFi
+@@ -90,12 +91,52 @@ export async function serveFile(filePath
+ }
+ }
+
++const CHECK_INTERVAL = 60000; // 60 seconds interval
+ const APP_ROOT = dirname(FileAccess.asFileUri('').fsPath);
+
++/**
++ * Checks for terminal activity by reading the /dev/pts directory and comparing modification times of the files.
++ *
++ * The /dev/pts directory is used in Unix-like operating systems to represent pseudo-terminal (PTY) devices.
++ * Each active terminal session is assigned a PTY device. These devices are represented as files within the /dev/pts directory.
++ * When a terminal session has activity, such as when a user inputs commands or output is written to the terminal,
++ * the modification time (mtime) of the corresponding PTY device file is updated. By monitoring the modification
++ * times of the files in the /dev/pts directory, we can detect terminal activity.
++ *
++ * If activity is detected (i.e., if any PTY device file was modified within the CHECK_INTERVAL), this function
++ * updates the last activity timestamp.
++ */
++const checkTerminalActivity = (idleFilePath: string) => {
++ fs.readdir('/dev/pts', (err, files) => {
++ if (err) {
++ console.error('Error reading /dev/pts directory:', err);
++ return;
++ }
++
++ const now = new Date();
++ const activityDetected = files.some((file) => {
++ const filePath = path.join('/dev/pts', file);
++ try {
++ const stats = fs.statSync(filePath);
++ const mtime = new Date(stats.mtime).getTime();
++ return now.getTime() - mtime < CHECK_INTERVAL;
++ } catch (error) {
++ console.error('Error reading file stats:', error);
++ return false;
++ }
++ });
++
++ if (activityDetected) {
++ fs.writeFileSync(idleFilePath, now.toISOString());
++ }
++ });
++};
++
const STATIC_PATH = `/static`;
const CALLBACK_PATH = `/callback`;
const WEB_EXTENSION_PATH = `/web-extension-resource`;
@@ -296,6 +288,7 @@ Index: sagemaker-code-editor/vscode/src/vs/server/node/webClientServer.ts
export class WebClientServer {
+ private readonly _webExtensionResourceUrlTemplate: URI | undefined;
@@ -131,6 +132,9 @@ export class WebClientServer {
// callback support
return this._handleCallback(res);
@@ -306,33 +299,36 @@ Index: sagemaker-code-editor/vscode/src/vs/server/node/webClientServer.ts
if (pathname.startsWith(WEB_EXTENSION_PATH) && pathname.charCodeAt(WEB_EXTENSION_PATH.length) === CharCode.Slash) {
// extension resource support
return this._handleWebExtensionResource(req, res, pathname.substring(WEB_EXTENSION_PATH.length));
-@@ -496,4 +500,29 @@ export class WebClientServer {
+@@ -496,4 +500,31 @@ export class WebClientServer {
});
return void res.end(data);
}
+
+ /**
-+ * Handles API requests to retrieve the last activity timestamp.
-+ */
-+ private async _handleIdle(req: http.IncomingMessage, res: http.ServerResponse): Promise {
-+ try {
-+ const tmpDirectory = '/tmp/'
-+ const idleFilePath = join(tmpDirectory, '.sagemaker-last-active-timestamp');
-+
-+ // If idle shutdown file does not exist, this indicates the app UI may never been opened
-+ // Create the initial metadata file
-+ if (!existsSync(idleFilePath)) {
-+ const timestamp = new Date().toISOString();
-+ writeFileSync(idleFilePath, timestamp);
-+ }
-+
-+ const data = await promises.readFile(idleFilePath, 'utf8');
-+
-+ res.statusCode = 200;
-+ res.setHeader('Content-Type', 'application/json');
-+ res.end(JSON.stringify({ lastActiveTimestamp: data }));
-+ } catch (error) {
-+ serveError(req, res, 500, error.message)
-+ }
-+ }
++ * Handles API requests to retrieve the last activity timestamp.
++ */
++ private async _handleIdle(req: http.IncomingMessage, res: http.ServerResponse): Promise {
++ try {
++ const tmpDirectory = '/tmp/'
++ const idleFilePath = path.join(tmpDirectory, '.sagemaker-last-active-timestamp');
++
++ // If idle shutdown file does not exist, this indicates the app UI may never been opened
++ // Create the initial metadata file
++ if (!existsSync(idleFilePath)) {
++ const timestamp = new Date().toISOString();
++ writeFileSync(idleFilePath, timestamp);
++ }
++
++ checkTerminalActivity(idleFilePath);
++
++ const data = await readFile(idleFilePath, 'utf8');
++
++ res.statusCode = 200;
++ res.setHeader('Content-Type', 'application/json');
++ res.end(JSON.stringify({ lastActiveTimestamp: data }));
++ } catch (error) {
++ serveError(req, res, 500, error.message);
++ }
++ }
}
+
\ No newline at end of file
diff --git a/patches/webview.diff b/patches/webview.diff
index 25f77c266..6d96dc0de 100644
--- a/patches/webview.diff
+++ b/patches/webview.diff
@@ -74,10 +74,19 @@ Index: sagemaker-code-editor/vscode/src/vs/workbench/contrib/webview/browser/pre
-+ content="default-src 'none'; script-src 'sha256-1qYtPnTQa4VwKNJO61EOhs2agF9TvuQSYIJ27OgzZqI=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
++ content="default-src 'none'; script-src 'sha256-Oi71Tq4Buohx0KDH3yEbVJUzABnqYv9iVLo420HZXqI=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
{
+ /**
+ * @param {MessageEvent} event
@@ -344,6 +344,12 @@
const hostname = location.hostname;