From 25e6f8414a51af3a17c4f8c818396205022890d7 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Tue, 15 Dec 2020 15:59:12 -0700 Subject: [PATCH] feat: remove vscode scripts used with submodule --- ci/dev/diff-vscode.sh | 12 - ci/dev/patch-vscode.sh | 11 - ci/dev/vscode.patch | 4995 ---------------------------------------- package.json | 2 - 4 files changed, 5020 deletions(-) delete mode 100755 ci/dev/diff-vscode.sh delete mode 100755 ci/dev/patch-vscode.sh delete mode 100644 ci/dev/vscode.patch diff --git a/ci/dev/diff-vscode.sh b/ci/dev/diff-vscode.sh deleted file mode 100755 index 99a643cf9..000000000 --- a/ci/dev/diff-vscode.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -main() { - cd "$(dirname "$0")/../.." - - cd ./lib/vscode - git add -A - git diff HEAD --patience --full-index > ../../ci/dev/vscode.patch -} - -main "$@" diff --git a/ci/dev/patch-vscode.sh b/ci/dev/patch-vscode.sh deleted file mode 100755 index cdc2691ad..000000000 --- a/ci/dev/patch-vscode.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -main() { - cd "$(dirname "$0")/../.." - - cd ./lib/vscode - git apply ../../ci/dev/vscode.patch -} - -main "$@" diff --git a/ci/dev/vscode.patch b/ci/dev/vscode.patch deleted file mode 100644 index b001441d4..000000000 --- a/ci/dev/vscode.patch +++ /dev/null @@ -1,4995 +0,0 @@ -diff --git a/.eslintignore b/.eslintignore -index b2c4a5b6efa856968bbb62c56113004ddb3929e6..035aec8f6a8b0ea8fc6d3d836d160da54aac2fdb 100644 ---- a/.eslintignore -+++ b/.eslintignore -@@ -14,3 +14,5 @@ - **/extensions/**/build/** - **/extensions/markdown-language-features/media/** - **/extensions/typescript-basics/test/colorize-fixtures/** -+# This is a code-server code symlink. -+src/vs/base/node/proxy_agent.ts -diff --git a/.eslintrc.json b/.eslintrc.json -index 055bc22f8e48e7dee559b83ac56c12a54c6ad544..14c026c04a7df5ac94bea2856e3a7a513c213775 100644 ---- a/.eslintrc.json -+++ b/.eslintrc.json -@@ -64,7 +64,7 @@ - "code-no-standalone-editor": "warn", - "code-no-unexternalized-strings": "warn", - "code-layering": [ -- "warn", -+ "off", - { - "common": [], - "node": [ -@@ -90,7 +90,7 @@ - } - ], - "code-import-patterns": [ -- "warn", -+ "off", - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // !!! Do not relax these rules !!! - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -diff --git a/.gitignore b/.gitignore -index b7f5b58c8ede171be547c56b61ce76f79a3accc3..856fbd8c67460fe099d7fbee1475e906b500f053 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -25,7 +25,6 @@ out-vscode-reh-web-pkg/ - out-vscode-web/ - out-vscode-web-min/ - out-vscode-web-pkg/ --src/vs/server - resources/server - build/node_modules - coverage/ -diff --git a/.yarnrc b/.yarnrc -deleted file mode 100644 -index d97527dab46aa4e7aa2df386bda3a8b4f93fcb80..0000000000000000000000000000000000000000 ---- a/.yarnrc -+++ /dev/null -@@ -1,3 +0,0 @@ --disturl "https://electronjs.org/headers" --target "9.3.3" --runtime "electron" -diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js -index 5f367d1f0777d2cb46ad47e376337900733981b5..ba74af1d61a00ce42020418126e62879397f57bf 100644 ---- a/build/gulpfile.reh.js -+++ b/build/gulpfile.reh.js -@@ -44,6 +44,7 @@ BUILD_TARGETS.forEach(({ platform, arch }) => { - }); - - function getNodeVersion() { -+ return process.versions.node; - const yarnrc = fs.readFileSync(path.join(REPO_ROOT, 'remote', '.yarnrc'), 'utf8'); - const target = /^target "(.*)"$/m.exec(yarnrc)[1]; - return target; -diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts -index dac71c814798ecfac99750be856078e043d239bf..6edd7ea56baef7cd9f87a9020df32d3b8519b615 100644 ---- a/build/lib/extensions.ts -+++ b/build/lib/extensions.ts -@@ -70,7 +70,7 @@ function fromLocal(extensionPath: string, forWeb: boolean): Stream { - if (isWebPacked) { - input = updateExtensionPackageJSON(input, (data: any) => { - delete data.scripts; -- delete data.dependencies; -+ // https://github.com/cdr/code-server/pull/2041#issuecomment-685910322 - delete data.devDependencies; - if (data.main) { - data.main = data.main.replace('/out/', /dist/); -diff --git a/build/lib/node.ts b/build/lib/node.ts -index 64397034461b1661f82007c141cbf4c039a3b722..c53dccf4dc0a99122ed96cf10c2eb632bb25059e 100644 ---- a/build/lib/node.ts -+++ b/build/lib/node.ts -@@ -4,13 +4,10 @@ - *--------------------------------------------------------------------------------------------*/ - - import * as path from 'path'; --import * as fs from 'fs'; - - const root = path.dirname(path.dirname(__dirname)); --const yarnrcPath = path.join(root, 'remote', '.yarnrc'); --const yarnrc = fs.readFileSync(yarnrcPath, 'utf8'); --const version = /^target\s+"([^"]+)"$/m.exec(yarnrc)![1]; -+const version = process.versions.node; - const node = process.platform === 'win32' ? 'node.exe' : 'node'; - const nodePath = path.join(root, '.build', 'node', `v${version}`, `${process.platform}-${process.arch}`, node); - --console.log(nodePath); -\ No newline at end of file -+console.log(nodePath); -diff --git a/build/lib/util.ts b/build/lib/util.ts -index c0a0d9619d736c6558b0b91e6c7537c1a06cc947..48853bc6201a602cadbef47a8f46281be93421e9 100644 ---- a/build/lib/util.ts -+++ b/build/lib/util.ts -@@ -336,6 +336,7 @@ export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise { - } - - export function getElectronVersion(): string { -+ return process.versions.node; - const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8'); - const target = /^target "(.*)"$/m.exec(yarnrc)![1]; - return target; -diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js -index 8f8b0019a7792a993fbd6bf95b013b596aa2935a..ea054c725bea2eec342e12b07314241aa18a4951 100644 ---- a/build/npm/postinstall.js -+++ b/build/npm/postinstall.js -@@ -33,10 +33,11 @@ function yarnInstall(location, opts) { - - yarnInstall('extensions'); // node modules shared by all extensions - --if (!(process.platform === 'win32' && (process.arch === 'arm64' || process.env['npm_config_arch'] === 'arm64'))) { -- yarnInstall('remote'); // node modules used by vscode server -- yarnInstall('remote/web'); // node modules used by vscode web --} -+// NOTE@coder: Skip these dependencies since we don't use them. -+// if (!(process.platform === 'win32' && (process.arch === 'arm64' || process.env['npm_config_arch'] === 'arm64'))) { -+// yarnInstall('remote'); // node modules used by vscode server -+// yarnInstall('remote/web'); // node modules used by vscode web -+// } - - const allExtensionFolders = fs.readdirSync('extensions'); - const extensions = allExtensionFolders.filter(e => { -@@ -69,9 +70,9 @@ runtime "${runtime}"`; - } - - yarnInstall(`build`); // node modules required for build --yarnInstall('test/automation'); // node modules required for smoketest --yarnInstall('test/smoke'); // node modules required for smoketest --yarnInstall('test/integration/browser'); // node modules required for integration -+// yarnInstall('test/automation'); // node modules required for smoketest -+// yarnInstall('test/smoke'); // node modules required for smoketest -+// yarnInstall('test/integration/browser'); // node modules required for integration - yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron - - cp.execSync('git config pull.rebase true'); -diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js -index cb88d37adefd4882f61a2711fdd7f72b89e1a6e3..6b3253af0a3a0aa4d75456379ef1c00f4cb98d13 100644 ---- a/build/npm/preinstall.js -+++ b/build/npm/preinstall.js -@@ -8,8 +8,9 @@ let err = false; - const majorNodeVersion = parseInt(/^(\d+)\./.exec(process.versions.node)[1]); - - if (majorNodeVersion < 10 || majorNodeVersion >= 13) { -- console.error('\033[1;31m*** Please use node >=10 and <=12.\033[0;0m'); -- err = true; -+ // We are ok building above Node 12. -+ // console.error('\033[1;31m*** Please use node >=10 and <=12.\033[0;0m'); -+ // err = true; - } - - const cp = require('child_process'); -diff --git a/coder.js b/coder.js -new file mode 100644 -index 0000000000000000000000000000000000000000..ac2e6eb2cdf41787194f7681a76ad4a55c5aeb5a ---- /dev/null -+++ b/coder.js -@@ -0,0 +1,64 @@ -+// This must be ran from VS Code's root. -+const gulp = require("gulp"); -+const path = require("path"); -+const _ = require("underscore"); -+const buildfile = require("./src/buildfile"); -+const common = require("./build/lib/optimize"); -+const util = require("./build/lib/util"); -+const deps = require("./build/dependencies"); -+ -+const vscodeEntryPoints = _.flatten([ -+ buildfile.entrypoint("vs/workbench/workbench.web.api"), -+ buildfile.entrypoint("vs/server/entry"), -+ buildfile.base, -+ buildfile.workbenchWeb, -+ buildfile.workerExtensionHost, -+ buildfile.workerNotebook, -+ buildfile.keyboardMaps, -+ buildfile.entrypoint("vs/platform/files/node/watcher/unix/watcherApp"), -+ buildfile.entrypoint("vs/platform/files/node/watcher/nsfw/watcherApp"), -+ buildfile.entrypoint("vs/workbench/services/extensions/node/extensionHostProcess"), -+]); -+ -+const vscodeResources = [ -+ "out-build/vs/server/fork.js", -+ "!out-build/vs/server/doc/**", -+ "out-build/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js", -+ "out-build/bootstrap.js", -+ "out-build/bootstrap-fork.js", -+ "out-build/bootstrap-amd.js", -+ 'out-build/bootstrap-node.js', -+ "out-build/paths.js", -+ 'out-build/vs/**/*.{svg,png,html,ttf}', -+ "!out-build/vs/code/browser/workbench/*.html", -+ '!out-build/vs/code/electron-browser/**', -+ "out-build/vs/base/common/performance.js", -+ "out-build/vs/base/node/languagePacks.js", -+ 'out-build/vs/base/browser/ui/codicons/codicon/**', -+ "out-build/vs/workbench/browser/media/*-theme.css", -+ "out-build/vs/workbench/contrib/debug/**/*.json", -+ "out-build/vs/workbench/contrib/externalTerminal/**/*.scpt", -+ "out-build/vs/workbench/contrib/webview/browser/pre/*.js", -+ "out-build/vs/**/markdown.css", -+ "out-build/vs/workbench/contrib/tasks/**/*.json", -+ "out-build/vs/platform/files/**/*.md", -+ "!**/test/**" -+]; -+ -+gulp.task("optimize", gulp.series( -+ util.rimraf("out-vscode"), -+ common.optimizeTask({ -+ src: "out-build", -+ entryPoints: vscodeEntryPoints, -+ resources: vscodeResources, -+ loaderConfig: common.loaderConfig(), -+ out: "out-vscode", -+ inlineAmdImages: true, -+ bundleInfo: undefined -+ }), -+)); -+ -+gulp.task("minify", gulp.series( -+ util.rimraf("out-vscode-min"), -+ common.minifyTask("out-vscode") -+)); -diff --git a/extensions/postinstall.js b/extensions/postinstall.js -index da4fa3e9d0443d679dfbab1000b434af2ae01afd..50f3e1144f8057883dea8b91ec2f7073458dbd94 100644 ---- a/extensions/postinstall.js -+++ b/extensions/postinstall.js -@@ -24,6 +24,9 @@ function processRoot() { - rimraf.sync(filePath); - } - } -+ -+ // Delete .bin so it doesn't contain broken symlinks that trip up nfpm. -+ rimraf.sync(path.join(__dirname, 'node_modules', '.bin')); - } - - function processLib() { -diff --git a/extensions/typescript-language-features/src/utils/platform.ts b/extensions/typescript-language-features/src/utils/platform.ts -index 2d754bf4054713f53beed030f9211b33532c1b4b..708b7e40a662e4ca93420992bf7a5af0c62ea5b2 100644 ---- a/extensions/typescript-language-features/src/utils/platform.ts -+++ b/extensions/typescript-language-features/src/utils/platform.ts -@@ -6,6 +6,6 @@ - import * as vscode from 'vscode'; - - export function isWeb(): boolean { -- // @ts-expect-error -+ // NOTE@coder: Remove unused ts-expect-error directive which causes tsc to error. - return typeof navigator !== 'undefined' && vscode.env.uiKind === vscode.UIKind.Web; - } -diff --git a/package.json b/package.json -index 28f8a69a2a91f9cb9f4dbd73ed3e689b2b3afe84..b5b0612dc8a5bea6b71cb0c40f21c8dc80582e69 100644 ---- a/package.json -+++ b/package.json -@@ -46,7 +46,11 @@ - "watch-web": "gulp watch-web --max_old_space_size=4095", - "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions" - }, -+ "dependencies_comment": "Move rimraf to dependencies because it is used in the postinstall script.", - "dependencies": { -+ "@coder/logger": "1.1.16", -+ "@coder/node-browser": "^1.0.8", -+ "@coder/requirefs": "^1.1.5", - "applicationinsights": "1.0.8", - "chokidar": "3.4.3", - "graceful-fs": "4.2.3", -@@ -60,6 +64,8 @@ - "native-keymap": "2.2.0", - "native-watchdog": "1.3.0", - "node-pty": "0.10.0-beta17", -+ "proxy-agent": "^4.0.0", -+ "rimraf": "^2.2.8", - "spdlog": "^0.11.1", - "sudo-prompt": "9.1.1", - "tas-client-umd": "0.1.2", -@@ -161,7 +167,6 @@ - "pump": "^1.0.1", - "queue": "3.0.6", - "rcedit": "^1.1.0", -- "rimraf": "^2.2.8", - "sinon": "^1.17.2", - "source-map": "^0.4.4", - "style-loader": "^1.0.0", -@@ -193,5 +198,8 @@ - "windows-foreground-love": "0.2.0", - "windows-mutex": "0.3.0", - "windows-process-tree": "0.2.4" -+ }, -+ "resolutions": { -+ "minimist": "^1.2.5" - } - } -diff --git a/product.json b/product.json -index 7cab6d1b9f3b84bfc703856e93773a293fd198cf..6924d94f65b390f52885b1036f7e96bce0e34680 100644 ---- a/product.json -+++ b/product.json -@@ -1,6 +1,6 @@ - { -- "nameShort": "Code - OSS", -- "nameLong": "Code - OSS", -+ "nameShort": "code-server", -+ "nameLong": "code-server", - "applicationName": "code-oss", - "dataFolderName": ".vscode-oss", - "win32MutexName": "vscodeoss", -@@ -20,7 +20,7 @@ - "darwinBundleIdentifier": "com.visualstudio.code.oss", - "linuxIconName": "com.visualstudio.code.oss", - "licenseFileName": "LICENSE.txt", -- "reportIssueUrl": "https://github.com/microsoft/vscode/issues/new", -+ "reportIssueUrl": "https://github.com/cdr/code-server/issues/new", - "urlProtocol": "code-oss", - "extensionAllowedProposedApi": [ - "ms-vscode.vscode-js-profile-flame", -@@ -136,5 +136,14 @@ - "publisherDisplayName": "Microsoft" - } - } -- ] -+ ], -+ -+ "//": "https://github.com/VSCodium/vscodium/pull/155/files", -+ "documentationUrl": "https://go.microsoft.com/fwlink/?LinkID=533484#vscode", -+ "keyboardShortcutsUrlMac": "https://go.microsoft.com/fwlink/?linkid=832143", -+ "keyboardShortcutsUrlLinux": "https://go.microsoft.com/fwlink/?linkid=832144", -+ "keyboardShortcutsUrlWin": "https://go.microsoft.com/fwlink/?linkid=832145", -+ "introductoryVideosUrl": "https://go.microsoft.com/fwlink/?linkid=832146", -+ "tipsAndTricksUrl": "https://go.microsoft.com/fwlink/?linkid=852118", -+ "newsletterSignupUrl": "https://www.research.net/r/vsc-newsletter" - } -diff --git a/remote/.yarnrc b/remote/.yarnrc -deleted file mode 100644 -index c1a32ce532afa501fb19bdbcf6bcb0ec151ecd99..0000000000000000000000000000000000000000 ---- a/remote/.yarnrc -+++ /dev/null -@@ -1,3 +0,0 @@ --disturl "http://nodejs.org/dist" --target "12.14.1" --runtime "node" -diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts -index f475b10e5e81d5c2511d8d36ca5fa30a54bc415a..e9a30b2cd2a7848241d9a430c28faccb51efdb9b 100644 ---- a/src/vs/base/common/network.ts -+++ b/src/vs/base/common/network.ts -@@ -113,16 +113,17 @@ class RemoteAuthoritiesImpl { - if (host && host.indexOf(':') !== -1) { - host = `[${host}]`; - } -- const port = this._ports[authority]; -+ // const port = this._ports[authority]; - const connectionToken = this._connectionTokens[authority]; - let query = `path=${encodeURIComponent(uri.path)}`; - if (typeof connectionToken === 'string') { - query += `&tkn=${encodeURIComponent(connectionToken)}`; - } -+ // NOTE@coder: Changed this to work against the current path. - return URI.from({ - scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource, -- authority: `${host}:${port}`, -- path: `/vscode-remote-resource`, -+ authority: window.location.host, -+ path: `${window.location.pathname.replace(/\/+$/, '')}/vscode-remote-resource`, - query - }); - } -diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts -index 3361d83be5b7c3d08bdbfbe6947942a4695882c6..69ead8484e042bbad7075659f8e47f074bc217e4 100644 ---- a/src/vs/base/common/platform.ts -+++ b/src/vs/base/common/platform.ts -@@ -71,6 +71,18 @@ if (typeof navigator === 'object' && !isElectronRenderer) { - _isWeb = true; - _locale = navigator.language; - _language = _locale; -+ -+ // NOTE@coder: Make languages work. -+ 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); -+ _locale = nlsConfig.locale; -+ _translationsConfigFile = nlsConfig._translationsConfigFile; -+ _language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT; -+ } catch (error) { /* Oh well. */ } -+ } - } - - // Native environment -diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts -index 17895a8510bca40924524dc107c33305c4783c45..ba019b43084e3998ab399108968c3c765a79eb32 100644 ---- a/src/vs/base/common/processes.ts -+++ b/src/vs/base/common/processes.ts -@@ -112,6 +112,7 @@ export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve - /^VSCODE_.+$/, - /^SNAP(|_.*)$/, - /^GDK_PIXBUF_.+$/, -+ /^CODE_SERVER_.+$/, - ]; - const envKeys = Object.keys(env); - envKeys -diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts -index ef2291d49b13c9c995afc90eab9c92afabc2b3b4..29b2f9dfc2b7fa998ac1188db06dee95419fcd5b 100644 ---- a/src/vs/base/common/uriIpc.ts -+++ b/src/vs/base/common/uriIpc.ts -@@ -5,6 +5,7 @@ - - import { URI, UriComponents } from 'vs/base/common/uri'; - import { MarshalledObject } from 'vs/base/common/marshalling'; -+import { Schemas } from './network'; - - export interface IURITransformer { - transformIncoming(uri: UriComponents): UriComponents; -@@ -31,29 +32,35 @@ function toJSON(uri: URI): UriComponents { - - export class URITransformer implements IURITransformer { - -- private readonly _uriTransformer: IRawURITransformer; -- -- constructor(uriTransformer: IRawURITransformer) { -- this._uriTransformer = uriTransformer; -+ constructor(private readonly remoteAuthority: string) { - } - -+ // NOTE@coder: Coming in from the browser it'll be vscode-remote so it needs -+ // to be transformed into file. - public transformIncoming(uri: UriComponents): UriComponents { -- const result = this._uriTransformer.transformIncoming(uri); -- return (result === uri ? uri : toJSON(URI.from(result))); -+ return uri.scheme === Schemas.vscodeRemote -+ ? toJSON(URI.file(uri.path)) -+ : uri; - } - -+ // NOTE@coder: Going out to the browser it'll be file so it needs to be -+ // transformed into vscode-remote. - public transformOutgoing(uri: UriComponents): UriComponents { -- const result = this._uriTransformer.transformOutgoing(uri); -- return (result === uri ? uri : toJSON(URI.from(result))); -+ return uri.scheme === Schemas.file -+ ? toJSON(URI.from({ authority: this.remoteAuthority, scheme: Schemas.vscodeRemote, path: uri.path })) -+ : uri; - } - - public transformOutgoingURI(uri: URI): URI { -- const result = this._uriTransformer.transformOutgoing(uri); -- return (result === uri ? uri : URI.from(result)); -+ return uri.scheme === Schemas.file -+ ? URI.from({ authority: this.remoteAuthority, scheme: Schemas.vscodeRemote, path:uri.path }) -+ : uri; - } - - public transformOutgoingScheme(scheme: string): string { -- return this._uriTransformer.transformOutgoingScheme(scheme); -+ return scheme === Schemas.file -+ ? Schemas.vscodeRemote -+ : scheme; - } - } - -@@ -152,4 +159,4 @@ export function transformAndReviveIncomingURIs(obj: T, transformer: IURITrans - return obj; - } - return result; --} -\ No newline at end of file -+} -diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js -index 2c64061da7b01aef0bfe3cec851da232ca9461c8..db47fe2eb1cded1e9c33b42fe700421c36a1d481 100644 ---- a/src/vs/base/node/languagePacks.js -+++ b/src/vs/base/node/languagePacks.js -@@ -128,7 +128,10 @@ function factory(nodeRequire, path, fs, perf) { - function getLanguagePackConfigurations(userDataPath) { - const configFile = path.join(userDataPath, 'languagepacks.json'); - try { -- return nodeRequire(configFile); -+ // NOTE@coder: Swapped require with readFile since require is cached and -+ // we don't restart the server-side portion of code-server when the -+ // language changes. -+ return JSON.parse(fs.readFileSync(configFile, 'utf8')); - } catch (err) { - // Do nothing. If we can't read the file we have no - // language pack config. -diff --git a/src/vs/base/node/proxy_agent.ts b/src/vs/base/node/proxy_agent.ts -new file mode 120000 -index 0000000000000000000000000000000000000000..3fd94e050108d5c52ad8a104cb5e048424ba192e ---- /dev/null -+++ b/src/vs/base/node/proxy_agent.ts -@@ -0,0 +1 @@ -+../../../../../../src/node/proxy_agent.ts -\ No newline at end of file -diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts -index 0ef8b9dc81419b53b27cf111fb206d72ba56bada..779971e2cc6e5394cdd4b079630238e80b352f33 100644 ---- a/src/vs/code/browser/workbench/workbench.ts -+++ b/src/vs/code/browser/workbench/workbench.ts -@@ -3,7 +3,7 @@ - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - --import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, IHomeIndicator, IProductQualityChangeHandler, ISettingsSyncOptions } from 'vs/workbench/workbench.web.api'; -+import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, IProductQualityChangeHandler, ISettingsSyncOptions } from 'vs/workbench/workbench.web.api'; - import { URI, UriComponents } from 'vs/base/common/uri'; - import { Event, Emitter } from 'vs/base/common/event'; - import { generateUuid } from 'vs/base/common/uuid'; -@@ -17,6 +17,7 @@ import { isStandalone } from 'vs/base/browser/browser'; - import { localize } from 'vs/nls'; - import { Schemas } from 'vs/base/common/network'; - import product from 'vs/platform/product/common/product'; -+import { encodePath } from 'vs/server/node/util'; - - function doCreateUri(path: string, queryValues: Map): URI { - let query: string | undefined = undefined; -@@ -309,12 +310,18 @@ class WorkspaceProvider implements IWorkspaceProvider { - - // Folder - else if (isFolderToOpen(workspace)) { -- targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${encodeURIComponent(workspace.folderUri.toString())}`; -+ const target = workspace.folderUri.scheme === Schemas.vscodeRemote -+ ? encodePath(workspace.folderUri.path) -+ : encodeURIComponent(workspace.folderUri.toString()); -+ targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${target}`; - } - - // Workspace - else if (isWorkspaceToOpen(workspace)) { -- targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${encodeURIComponent(workspace.workspaceUri.toString())}`; -+ const target = workspace.workspaceUri.scheme === Schemas.vscodeRemote -+ ? encodePath(workspace.workspaceUri.path) -+ : encodeURIComponent(workspace.workspaceUri.toString()); -+ targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${target}`; - } - - // Append payload if any -@@ -404,7 +411,22 @@ class WindowIndicator implements IWindowIndicator { - throw new Error('Missing web configuration element'); - } - -- const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(configElementAttribute); -+ const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = { -+ webviewEndpoint: `${window.location.origin}${window.location.pathname.replace(/\/+$/, '')}/webview`, -+ ...JSON.parse(configElementAttribute), -+ }; -+ -+ // Strip the protocol from the authority if it exists. -+ const normalizeAuthority = (authority: string): string => authority.replace(/^https?:\/\//, ''); -+ if (config.remoteAuthority) { -+ (config as any).remoteAuthority = normalizeAuthority(config.remoteAuthority); -+ } -+ if (config.workspaceUri && config.workspaceUri.authority) { -+ config.workspaceUri.authority = normalizeAuthority(config.workspaceUri.authority); -+ } -+ if (config.folderUri && config.folderUri.authority) { -+ config.folderUri.authority = normalizeAuthority(config.folderUri.authority); -+ } - - // Revive static extension locations - if (Array.isArray(config.staticExtensions)) { -@@ -416,40 +438,7 @@ class WindowIndicator implements IWindowIndicator { - // Find workspace to open and payload - let foundWorkspace = false; - let workspace: IWorkspace; -- let payload = Object.create(null); -- -- const query = new URL(document.location.href).searchParams; -- query.forEach((value, key) => { -- switch (key) { -- -- // Folder -- case WorkspaceProvider.QUERY_PARAM_FOLDER: -- workspace = { folderUri: URI.parse(value) }; -- foundWorkspace = true; -- break; -- -- // Workspace -- case WorkspaceProvider.QUERY_PARAM_WORKSPACE: -- workspace = { workspaceUri: URI.parse(value) }; -- foundWorkspace = true; -- break; -- -- // Empty -- case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW: -- workspace = undefined; -- foundWorkspace = true; -- break; -- -- // Payload -- case WorkspaceProvider.QUERY_PARAM_PAYLOAD: -- try { -- payload = JSON.parse(value); -- } catch (error) { -- console.error(error); // possible invalid JSON -- } -- break; -- } -- }); -+ let payload = config.workspaceProvider?.payload || Object.create(null); - - // If no workspace is provided through the URL, check for config attribute from server - if (!foundWorkspace) { -@@ -465,13 +454,6 @@ class WindowIndicator implements IWindowIndicator { - // Workspace Provider - const workspaceProvider = new WorkspaceProvider(workspace, payload); - -- // Home Indicator -- const homeIndicator: IHomeIndicator = { -- href: 'https://github.com/microsoft/vscode', -- icon: 'code', -- title: localize('home', "Home") -- }; -- - // Window indicator (unless connected to a remote) - let windowIndicator: WindowIndicator | undefined = undefined; - if (!workspaceProvider.hasRemote()) { -@@ -515,7 +497,6 @@ class WindowIndicator implements IWindowIndicator { - create(document.body, { - ...config, - settingsSyncOptions, -- homeIndicator, - windowIndicator, - productQualityChangeHandler, - workspaceProvider, -diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts -index 409bb7e1960c9c06485a6f6d7f39b2efce451d56..f27b651c49ea3fc92b03e31eb64c1cf27c7e4433 100644 ---- a/src/vs/platform/environment/common/argv.ts -+++ b/src/vs/platform/environment/common/argv.ts -@@ -7,6 +7,8 @@ - * A list of command line arguments we support natively. - */ - export interface NativeParsedArgs { -+ 'extra-extensions-dir'?: string[]; -+ 'extra-builtin-extensions-dir'?: string[]; - _: string[]; - 'folder-uri'?: string[]; // undefined or array of 1 or more - 'file-uri'?: string[]; // undefined or array of 1 or more -diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts -index 21b4d719cec1a724bbad407aeec38db9eb8d6f5a..edf46f097bf11bfb8883d38d38ee78b735f35b3f 100644 ---- a/src/vs/platform/environment/common/environment.ts -+++ b/src/vs/platform/environment/common/environment.ts -@@ -122,6 +122,8 @@ export interface INativeEnvironmentService extends IEnvironmentService { - extensionsPath?: string; - extensionsDownloadPath: string; - builtinExtensionsPath: string; -+ extraExtensionPaths: string[] -+ extraBuiltinExtensionPaths: string[] - - // --- Smoke test support - driverHandle?: string; -diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts -index 149e6ffb41a82f1a69cf37f105a31872ad4af8b4..ed99aab42b31bc2ab804391b6e3f4c7ff67d9259 100644 ---- a/src/vs/platform/environment/node/argv.ts -+++ b/src/vs/platform/environment/node/argv.ts -@@ -54,6 +54,8 @@ export const OPTIONS: OptionDescriptions> = { - 'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") }, - 'extensions-download-dir': { type: 'string' }, - 'builtin-extensions-dir': { type: 'string' }, -+ 'extra-builtin-extensions-dir': { type: 'string[]', cat: 'o', description: 'Path to an extra builtin extension directory.' }, -+ 'extra-extensions-dir': { type: 'string[]', cat: 'o', description: 'Path to an extra user extension directory.' }, - 'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, - 'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") }, - 'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") }, -@@ -318,4 +320,3 @@ export function buildHelpMessage(productName: string, executableName: string, ve - export function buildVersionMessage(version: string | undefined, commit: string | undefined): string { - return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`; - } -- -diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts -index 80f68fb1decfd1c4fa1bcc30840900240df83f76..d4478b0000a511af11647876a536b8147163f9f8 100644 ---- a/src/vs/platform/environment/node/environmentService.ts -+++ b/src/vs/platform/environment/node/environmentService.ts -@@ -138,6 +138,13 @@ export class NativeEnvironmentService implements INativeEnvironmentService { - return resources.joinPath(this.userHome, product.dataFolderName, 'extensions').fsPath; - } - -+ @memoize get extraExtensionPaths(): string[] { -+ return (this._args['extra-extensions-dir'] || []).map((p) => parsePathArg(p, process)); -+ } -+ @memoize get extraBuiltinExtensionPaths(): string[] { -+ return (this._args['extra-builtin-extensions-dir'] || []).map((p) => parsePathArg(p, process)); -+ } -+ - @memoize - get extensionDevelopmentLocationURI(): URI[] | undefined { - const s = this._args.extensionDevelopmentPath; -diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts -index aee65f8eddbfbce3e42362be9590c98d46f2ace5..dc891fba7c7af3ace02b0091ef858bea59e754c6 100644 ---- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts -+++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts -@@ -91,7 +91,7 @@ export class ExtensionsScanner extends Disposable { - } - - async scanAllUserExtensions(): Promise { -- return this.scanExtensionsInDir(this.extensionsPath, ExtensionType.User); -+ return this.scanExtensionsInDirs(this.extensionsPath, this.environmentService.extraExtensionPaths, ExtensionType.User); - } - - async extractUserExtension(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, token: CancellationToken): Promise { -@@ -236,7 +236,13 @@ export class ExtensionsScanner extends Disposable { - - private async scanExtensionsInDir(dir: string, type: ExtensionType): Promise { - const limiter = new Limiter(10); -- const extensionsFolders = await pfs.readdir(dir); -+ const extensionsFolders = await pfs.readdir(dir) -+ .catch((error) => { -+ if (error.code !== 'ENOENT') { -+ throw error; -+ } -+ return []; -+ }); - const extensions = await Promise.all(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, dir, type)))); - return extensions.filter(e => e && e.identifier); - } -@@ -266,7 +272,7 @@ export class ExtensionsScanner extends Disposable { - } - - private async scanDefaultSystemExtensions(): Promise { -- const result = await this.scanExtensionsInDir(this.systemExtensionsPath, ExtensionType.System); -+ const result = await this.scanExtensionsInDirs(this.systemExtensionsPath, this.environmentService.extraBuiltinExtensionPaths, ExtensionType.System); - this.logService.trace('Scanned system extensions:', result.length); - return result; - } -@@ -370,4 +376,9 @@ export class ExtensionsScanner extends Disposable { - } - }); - } -+ -+ private async scanExtensionsInDirs(dir: string, dirs: string[], type: ExtensionType): Promise{ -+ const results = await Promise.all([dir, ...dirs].map((path) => this.scanExtensionsInDir(path, type))); -+ return results.reduce((flat, current) => flat.concat(current), []); -+ } - } -diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts -index 2bea85740cb3e00c955ec0f7aa46d5f9bb8d5dc8..c0953d7b73178fc4a7b030246a5281609c3dfce6 100644 ---- a/src/vs/platform/product/common/product.ts -+++ b/src/vs/platform/product/common/product.ts -@@ -37,6 +37,12 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire ! - ], - }); - } -+ // NOTE@coder: Add the ability to inject settings from the server. -+ const el = document.getElementById('vscode-remote-product-configuration'); -+ const rawProductConfiguration = el && el.getAttribute('data-settings'); -+ if (rawProductConfiguration) { -+ Object.assign(product, JSON.parse(rawProductConfiguration)); -+ } - } - - // Native (non-sandboxed) -diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts -index 333e5b24b05c96e8d44e9025b7a777e6989de9e7..b13572327a6e91592eedea9bcb1e580397f5c224 100644 ---- a/src/vs/platform/product/common/productService.ts -+++ b/src/vs/platform/product/common/productService.ts -@@ -32,6 +32,8 @@ export type ConfigurationSyncStore = { - }; - - export interface IProductConfiguration { -+ readonly codeServerVersion?: string; -+ - readonly version: string; - readonly date?: string; - readonly quality?: string; -diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts -index 3715cbb8e6ee41c3d9b5090918d243b723ae2d00..c65de8ad37e727d66da97a8f8b170cbcef87181b 100644 ---- a/src/vs/platform/remote/browser/browserSocketFactory.ts -+++ b/src/vs/platform/remote/browser/browserSocketFactory.ts -@@ -208,7 +208,8 @@ export class BrowserSocketFactory implements ISocketFactory { - } - - connect(host: string, port: number, query: string, callback: IConnectCallback): void { -- const socket = this._webSocketFactory.create(`ws://${host}:${port}/?${query}&skipWebSocketFrames=false`); -+ // NOTE@coder: Modified to work against the current path. -+ const socket = this._webSocketFactory.create(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}${window.location.pathname}?${query}&skipWebSocketFrames=false`); - const errorListener = socket.onError((err) => callback(err, undefined)); - socket.onOpen(() => { - errorListener.dispose(); -@@ -216,6 +217,3 @@ export class BrowserSocketFactory implements ISocketFactory { - }); - } - } -- -- -- -diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts -index fdd5890c69f72025b94913380f0d226226e8c8fb..957b4812783f6b1d97d1003c63725f541042059f 100644 ---- a/src/vs/platform/remote/common/remoteAgentConnection.ts -+++ b/src/vs/platform/remote/common/remoteAgentConnection.ts -@@ -93,7 +93,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio - options.socketFactory.connect( - options.host, - options.port, -- `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, -+ `type=${connectionTypeToString(connectionType)}&reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, - (err: any, socket: ISocket | undefined) => { - if (err || !socket) { - options.logService.error(`${logPrefix} socketFactory.connect() failed. Error:`); -@@ -338,7 +338,8 @@ export class ReconnectionWaitEvent { - public readonly type = PersistentConnectionEventType.ReconnectionWait; - constructor( - public readonly durationSeconds: number, -- private readonly cancellableTimer: CancelablePromise -+ private readonly cancellableTimer: CancelablePromise, -+ public readonly connectionAttempt: number - ) { } - - public skipWait(): void { -@@ -422,7 +423,7 @@ abstract class PersistentConnection extends Disposable { - const waitTime = (attempt < TIMES.length ? TIMES[attempt] : TIMES[TIMES.length - 1]); - try { - const sleepPromise = sleep(waitTime); -- this._onDidStateChange.fire(new ReconnectionWaitEvent(waitTime, sleepPromise)); -+ this._onDidStateChange.fire(new ReconnectionWaitEvent(waitTime, sleepPromise, attempt+1)); - - this._options.logService.info(`${logPrefix} waiting for ${waitTime} seconds before reconnecting...`); - try { -diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts -index ab3fd347b69f8a3d9b96e706cd87c911b8ffed6b..9d351037b577f9f1edfd18ae9b3c48a211f4467f 100644 ---- a/src/vs/platform/storage/browser/storageService.ts -+++ b/src/vs/platform/storage/browser/storageService.ts -@@ -122,8 +122,8 @@ export class BrowserStorageService extends Disposable implements IStorageService - return this.getStorage(scope).getNumber(key, fallbackValue); - } - -- store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { -- this.getStorage(scope).set(key, value); -+ store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise { -+ return this.getStorage(scope).set(key, value); - } - - remove(key: string, scope: StorageScope): void { -diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts -index 6611f1dae42055f69a55c1c154d9475f11cd4d0a..d598d4909d5ff6d1614e4a038b1865e1f9a4e963 100644 ---- a/src/vs/platform/storage/common/storage.ts -+++ b/src/vs/platform/storage/common/storage.ts -@@ -85,7 +85,7 @@ export interface IStorageService { - * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. - */ -- store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void; -+ store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise | void; - - /** - * Delete an element stored under the provided key from storage. -diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts -index 096b9e23493539c9937940a56e555d95bbae38d9..ef37e614004f550f7b64eacd362f6894fc523a42 100644 ---- a/src/vs/platform/storage/node/storageService.ts -+++ b/src/vs/platform/storage/node/storageService.ts -@@ -201,8 +201,8 @@ export class NativeStorageService extends Disposable implements IStorageService - return this.getStorage(scope).getNumber(key, fallbackValue); - } - -- store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { -- this.getStorage(scope).set(key, value); -+ store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise { -+ return this.getStorage(scope).set(key, value); - } - - remove(key: string, scope: StorageScope): void { -diff --git a/src/vs/server/browser/client.ts b/src/vs/server/browser/client.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..8a92b722b38f8743403892ea97cfb2a2a8726e3b ---- /dev/null -+++ b/src/vs/server/browser/client.ts -@@ -0,0 +1,241 @@ -+import { Emitter } from 'vs/base/common/event'; -+import { URI } from 'vs/base/common/uri'; -+import { localize } from 'vs/nls'; -+import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -+import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -+import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -+import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -+import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -+import { Registry } from 'vs/platform/registry/common/platform'; -+import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; -+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -+import { INodeProxyService, NodeProxyChannelClient } from 'vs/server/common/nodeProxy'; -+import { TelemetryChannelClient } from 'vs/server/common/telemetry'; -+import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; -+import { LocalizationsService } from 'vs/workbench/services/localizations/electron-browser/localizationsService'; -+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -+import { Options } from 'vs/server/ipc.d'; -+import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -+import { ILogService } from 'vs/platform/log/common/log'; -+import * as path from 'vs/base/common/path'; -+ -+class TelemetryService extends TelemetryChannelClient { -+ public constructor( -+ @IRemoteAgentService remoteAgentService: IRemoteAgentService, -+ ) { -+ super(remoteAgentService.getConnection()!.getChannel('telemetry')); -+ } -+} -+ -+/** -+ * Remove extra slashes in a URL. -+ */ -+export const normalize = (url: string, keepTrailing = false): string => { -+ return url.replace(/\/\/+/g, '/').replace(/\/+$/, keepTrailing ? '/' : ''); -+}; -+ -+/** -+ * Get options embedded in the HTML. -+ */ -+export const getOptions = (): T => { -+ try { -+ return JSON.parse(document.getElementById('coder-options')!.getAttribute('data-settings')!); -+ } catch (error) { -+ return {} as T; -+ } -+}; -+ -+const options = getOptions(); -+ -+const TELEMETRY_SECTION_ID = 'telemetry'; -+Registry.as(Extensions.Configuration).registerConfiguration({ -+ 'id': TELEMETRY_SECTION_ID, -+ 'order': 110, -+ 'type': 'object', -+ 'title': localize('telemetryConfigurationTitle', 'Telemetry'), -+ 'properties': { -+ 'telemetry.enableTelemetry': { -+ 'type': 'boolean', -+ 'description': localize('telemetry.enableTelemetry', 'Enable usage data and errors to be sent to a Microsoft online service.'), -+ 'default': !options.disableTelemetry, -+ 'tags': ['usesOnlineServices'] -+ } -+ } -+}); -+ -+class NodeProxyService extends NodeProxyChannelClient implements INodeProxyService { -+ private readonly _onClose = new Emitter(); -+ public readonly onClose = this._onClose.event; -+ private readonly _onDown = new Emitter(); -+ public readonly onDown = this._onDown.event; -+ private readonly _onUp = new Emitter(); -+ public readonly onUp = this._onUp.event; -+ -+ public constructor( -+ @IRemoteAgentService remoteAgentService: IRemoteAgentService, -+ ) { -+ super(remoteAgentService.getConnection()!.getChannel('nodeProxy')); -+ remoteAgentService.getConnection()!.onDidStateChange((state) => { -+ switch (state.type) { -+ case PersistentConnectionEventType.ConnectionGain: -+ return this._onUp.fire(); -+ case PersistentConnectionEventType.ConnectionLost: -+ return this._onDown.fire(); -+ case PersistentConnectionEventType.ReconnectionPermanentFailure: -+ return this._onClose.fire(); -+ } -+ }); -+ } -+} -+ -+registerSingleton(ILocalizationsService, LocalizationsService); -+registerSingleton(INodeProxyService, NodeProxyService); -+registerSingleton(ITelemetryService, TelemetryService); -+ -+/** -+ * This is called by vs/workbench/browser/web.main.ts after the workbench has -+ * been initialized so we can initialize our own client-side code. -+ */ -+export const initialize = async (services: ServiceCollection): Promise => { -+ const event = new CustomEvent('ide-ready'); -+ window.dispatchEvent(event); -+ -+ if (parent) { -+ // Tell the parent loading has completed. -+ parent.postMessage({ event: 'loaded' }, window.location.origin); -+ -+ // Proxy or stop proxing events as requested by the parent. -+ const listeners = new Map void>(); -+ window.addEventListener('message', (parentEvent) => { -+ const eventName = parentEvent.data.bind || parentEvent.data.unbind; -+ if (eventName) { -+ const oldListener = listeners.get(eventName); -+ if (oldListener) { -+ document.removeEventListener(eventName, oldListener); -+ } -+ } -+ -+ if (parentEvent.data.bind && parentEvent.data.prop) { -+ const listener = (event: Event) => { -+ parent.postMessage({ -+ event: parentEvent.data.event, -+ [parentEvent.data.prop]: event[parentEvent.data.prop as keyof Event] -+ }, window.location.origin); -+ }; -+ listeners.set(parentEvent.data.bind, listener); -+ document.addEventListener(parentEvent.data.bind, listener); -+ } -+ }); -+ } -+ -+ if (!window.isSecureContext) { -+ (services.get(INotificationService) as INotificationService).notify({ -+ severity: Severity.Warning, -+ message: 'code-server is being accessed over an insecure domain. Web views, the clipboard, and other functionality will not work as expected.', -+ actions: { -+ primary: [{ -+ id: 'understand', -+ label: 'I understand', -+ tooltip: '', -+ class: undefined, -+ enabled: true, -+ checked: true, -+ dispose: () => undefined, -+ run: () => { -+ return Promise.resolve(); -+ } -+ }], -+ } -+ }); -+ } -+ -+ const logService = (services.get(ILogService) as ILogService); -+ const storageService = (services.get(IStorageService) as IStorageService); -+ const updateCheckEndpoint = path.join(options.base, '/update/check'); -+ const getUpdate = async (): Promise => { -+ logService.debug('Checking for update...'); -+ -+ const response = await fetch(updateCheckEndpoint, { -+ headers: { 'Accept': 'application/json' }, -+ }); -+ if (!response.ok) { -+ throw new Error(response.statusText); -+ } -+ const json = await response.json(); -+ if (json.error) { -+ throw new Error(json.error); -+ } -+ if (json.isLatest) { -+ return; -+ } -+ -+ const lastNoti = storageService.getNumber('csLastUpdateNotification', StorageScope.GLOBAL); -+ if (lastNoti) { -+ // Only remind them again after 1 week. -+ const timeout = 1000*60*60*24*7; -+ const threshold = lastNoti + timeout; -+ if (Date.now() < threshold) { -+ return; -+ } -+ } -+ -+ storageService.store('csLastUpdateNotification', Date.now(), StorageScope.GLOBAL); -+ (services.get(INotificationService) as INotificationService).notify({ -+ severity: Severity.Info, -+ message: `[code-server v${json.latest}](https://github.com/cdr/code-server/releases/tag/v${json.latest}) has been released!`, -+ }); -+ }; -+ -+ const updateLoop = (): void => { -+ getUpdate().catch((error) => { -+ logService.debug(`failed to check for update: ${error}`); -+ }).finally(() => { -+ // Check again every 6 hours. -+ setTimeout(updateLoop, 1000*60*60*6); -+ }); -+ }; -+ -+ if (!options.disableUpdateCheck) { -+ updateLoop(); -+ } -+ -+ // This will be used to set the background color while VS Code loads. -+ const theme = storageService.get('colorThemeData', StorageScope.GLOBAL); -+ if (theme) { -+ localStorage.setItem('colorThemeData', theme); -+ } -+}; -+ -+export interface Query { -+ [key: string]: string | undefined; -+} -+ -+/** -+ * Split a string up to the delimiter. If the delimiter doesn't exist the first -+ * item will have all the text and the second item will be an empty string. -+ */ -+export const split = (str: string, delimiter: string): [string, string] => { -+ const index = str.indexOf(delimiter); -+ return index !== -1 ? [str.substring(0, index).trim(), str.substring(index + 1)] : [str, '']; -+}; -+ -+/** -+ * Return the URL modified with the specified query variables. It's pretty -+ * stupid so it probably doesn't cover any edge cases. Undefined values will -+ * unset existing values. Doesn't allow duplicates. -+ */ -+export const withQuery = (url: string, replace: Query): string => { -+ const uri = URI.parse(url); -+ const query = { ...replace }; -+ uri.query.split('&').forEach((kv) => { -+ const [key, value] = split(kv, '='); -+ if (!(key in query)) { -+ query[key] = value; -+ } -+ }); -+ return uri.with({ -+ query: Object.keys(query) -+ .filter((k) => typeof query[k] !== 'undefined') -+ .map((k) => `${k}=${query[k]}`).join('&'), -+ }).toString(true); -+}; -diff --git a/src/vs/server/browser/extHostNodeProxy.ts b/src/vs/server/browser/extHostNodeProxy.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..5dd5406befcb593ad6366d9e98f46485ed14fbc0 ---- /dev/null -+++ b/src/vs/server/browser/extHostNodeProxy.ts -@@ -0,0 +1,51 @@ -+import { Emitter } from 'vs/base/common/event'; -+import { UriComponents } from 'vs/base/common/uri'; -+import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -+import { ExtHostNodeProxyShape, MainContext, MainThreadNodeProxyShape } from 'vs/workbench/api/common/extHost.protocol'; -+import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -+ -+export class ExtHostNodeProxy implements ExtHostNodeProxyShape { -+ _serviceBrand: any; -+ -+ private readonly _onMessage = new Emitter(); -+ public readonly onMessage = this._onMessage.event; -+ private readonly _onClose = new Emitter(); -+ public readonly onClose = this._onClose.event; -+ private readonly _onDown = new Emitter(); -+ public readonly onDown = this._onDown.event; -+ private readonly _onUp = new Emitter(); -+ public readonly onUp = this._onUp.event; -+ -+ private readonly proxy: MainThreadNodeProxyShape; -+ -+ constructor(@IExtHostRpcService rpc: IExtHostRpcService) { -+ this.proxy = rpc.getProxy(MainContext.MainThreadNodeProxy); -+ } -+ -+ public $onMessage(message: string): void { -+ this._onMessage.fire(message); -+ } -+ -+ public $onClose(): void { -+ this._onClose.fire(); -+ } -+ -+ public $onUp(): void { -+ this._onUp.fire(); -+ } -+ -+ public $onDown(): void { -+ this._onDown.fire(); -+ } -+ -+ public send(message: string): void { -+ this.proxy.$send(message); -+ } -+ -+ public async fetchExtension(extensionUri: UriComponents): Promise { -+ return this.proxy.$fetchExtension(extensionUri).then(b => b.buffer); -+ } -+} -+ -+export interface IExtHostNodeProxy extends ExtHostNodeProxy { } -+export const IExtHostNodeProxy = createDecorator('IExtHostNodeProxy'); -diff --git a/src/vs/server/browser/mainThreadNodeProxy.ts b/src/vs/server/browser/mainThreadNodeProxy.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..acabf8c167cabc954b8611a77f57641f3ca0b444 ---- /dev/null -+++ b/src/vs/server/browser/mainThreadNodeProxy.ts -@@ -0,0 +1,55 @@ -+import { VSBuffer } from 'vs/base/common/buffer'; -+import { IDisposable } from 'vs/base/common/lifecycle'; -+import { FileAccess } from 'vs/base/common/network'; -+import { URI, UriComponents } from 'vs/base/common/uri'; -+import { INodeProxyService } from 'vs/server/common/nodeProxy'; -+import { ExtHostContext, IExtHostContext, MainContext, MainThreadNodeProxyShape } from 'vs/workbench/api/common/extHost.protocol'; -+import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -+ -+@extHostNamedCustomer(MainContext.MainThreadNodeProxy) -+export class MainThreadNodeProxy implements MainThreadNodeProxyShape { -+ private disposed = false; -+ private disposables = []; -+ -+ constructor( -+ extHostContext: IExtHostContext, -+ @INodeProxyService private readonly proxyService: INodeProxyService, -+ ) { -+ if (!extHostContext.remoteAuthority) { // HACK: A terrible way to detect if running in the worker. -+ const proxy = extHostContext.getProxy(ExtHostContext.ExtHostNodeProxy); -+ this.disposables = [ -+ this.proxyService.onMessage((message: string) => proxy.$onMessage(message)), -+ this.proxyService.onClose(() => proxy.$onClose()), -+ this.proxyService.onDown(() => proxy.$onDown()), -+ this.proxyService.onUp(() => proxy.$onUp()), -+ ]; -+ } -+ } -+ -+ $send(message: string): void { -+ if (!this.disposed) { -+ this.proxyService.send(message); -+ } -+ } -+ -+ async $fetchExtension(extensionUri: UriComponents): Promise { -+ const fetchUri = URI.from({ -+ scheme: window.location.protocol.replace(':', ''), -+ authority: window.location.host, -+ // Use FileAccess to get the static base path. -+ path: FileAccess.asBrowserUri('', require).path, -+ query: `tar=${encodeURIComponent(extensionUri.path)}`, -+ }); -+ const response = await fetch(fetchUri.toString(true)); -+ if (response.status !== 200) { -+ throw new Error(`Failed to download extension "${module}"`); -+ } -+ return VSBuffer.wrap(new Uint8Array(await response.arrayBuffer())); -+ } -+ -+ dispose(): void { -+ this.disposables.forEach((d) => d.dispose()); -+ this.disposables = []; -+ this.disposed = true; -+ } -+} -diff --git a/src/vs/server/browser/worker.ts b/src/vs/server/browser/worker.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..1d47ede49b76b1774329269ab5c86fedb5712c19 ---- /dev/null -+++ b/src/vs/server/browser/worker.ts -@@ -0,0 +1,48 @@ -+import { Client } from '@coder/node-browser'; -+import { fromTar } from '@coder/requirefs'; -+import { URI } from 'vs/base/common/uri'; -+import { ILogService } from 'vs/platform/log/common/log'; -+import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator'; -+import { IExtHostNodeProxy } from './extHostNodeProxy'; -+ -+export const loadCommonJSModule = async ( -+ module: URI, -+ activationTimesBuilder: ExtensionActivationTimesBuilder, -+ nodeProxy: IExtHostNodeProxy, -+ logService: ILogService, -+ vscode: any, -+): Promise => { -+ const client = new Client(nodeProxy, { logger: logService }); -+ const [buffer, init] = await Promise.all([ -+ nodeProxy.fetchExtension(module), -+ client.handshake(), -+ ]); -+ const rfs = fromTar(buffer); -+ (self).global = self; -+ rfs.provide('vscode', vscode); -+ Object.keys(client.modules).forEach((key) => { -+ const mod = (client.modules as any)[key]; -+ if (key === 'process') { -+ (self).process = mod; -+ (self).process.env = init.env; -+ return; -+ } -+ -+ rfs.provide(key, mod); -+ switch (key) { -+ case 'buffer': -+ (self).Buffer = mod.Buffer; -+ break; -+ case 'timers': -+ (self).setImmediate = mod.setImmediate; -+ break; -+ } -+ }); -+ -+ try { -+ activationTimesBuilder.codeLoadingStart(); -+ return rfs.require('.'); -+ } finally { -+ activationTimesBuilder.codeLoadingStop(); -+ } -+}; -diff --git a/src/vs/server/common/nodeProxy.ts b/src/vs/server/common/nodeProxy.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..14b9de879ceab4c1976770fa7810d276c5aa3e36 ---- /dev/null -+++ b/src/vs/server/common/nodeProxy.ts -@@ -0,0 +1,47 @@ -+import { ReadWriteConnection } from '@coder/node-browser'; -+import { Event } from 'vs/base/common/event'; -+import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -+import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -+ -+export const INodeProxyService = createDecorator('nodeProxyService'); -+ -+export interface INodeProxyService extends ReadWriteConnection { -+ _serviceBrand: any; -+ send(message: string): void; -+ onMessage: Event; -+ onUp: Event; -+ onClose: Event; -+ onDown: Event; -+} -+ -+export class NodeProxyChannel implements IServerChannel { -+ constructor(private service: INodeProxyService) {} -+ -+ listen(_: unknown, event: string): Event { -+ switch (event) { -+ case 'onMessage': return this.service.onMessage; -+ } -+ throw new Error(`Invalid listen ${event}`); -+ } -+ -+ async call(_: unknown, command: string, args?: any): Promise { -+ switch (command) { -+ case 'send': return this.service.send(args[0]); -+ } -+ throw new Error(`Invalid call ${command}`); -+ } -+} -+ -+export class NodeProxyChannelClient { -+ _serviceBrand: any; -+ -+ public readonly onMessage: Event; -+ -+ constructor(private readonly channel: IChannel) { -+ this.onMessage = this.channel.listen('onMessage'); -+ } -+ -+ public send(data: string): void { -+ this.channel.call('send', [data]); -+ } -+} -diff --git a/src/vs/server/common/telemetry.ts b/src/vs/server/common/telemetry.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..4ea6d95d36aaac07dbd4d0e16ab3c1bba255f683 ---- /dev/null -+++ b/src/vs/server/common/telemetry.ts -@@ -0,0 +1,65 @@ -+import { ITelemetryData } from 'vs/base/common/actions'; -+import { Event } from 'vs/base/common/event'; -+import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -+import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; -+import { ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -+ -+export class TelemetryChannel implements IServerChannel { -+ constructor(private service: ITelemetryService) {} -+ -+ listen(_: unknown, event: string): Event { -+ throw new Error(`Invalid listen ${event}`); -+ } -+ -+ call(_: unknown, command: string, args?: any): Promise { -+ switch (command) { -+ case 'publicLog': return this.service.publicLog(args[0], args[1], args[2]); -+ case 'publicLog2': return this.service.publicLog2(args[0], args[1], args[2]); -+ case 'publicLogError': return this.service.publicLogError(args[0], args[1]); -+ case 'publicLogError2': return this.service.publicLogError2(args[0], args[1]); -+ case 'setEnabled': return Promise.resolve(this.service.setEnabled(args[0])); -+ case 'getTelemetryInfo': return this.service.getTelemetryInfo(); -+ case 'setExperimentProperty': return Promise.resolve(this.service.setExperimentProperty(args[0], args[1])); -+ } -+ throw new Error(`Invalid call ${command}`); -+ } -+} -+ -+export class TelemetryChannelClient implements ITelemetryService { -+ _serviceBrand: any; -+ -+ // These don't matter; telemetry is sent to the Node side which decides -+ // whether to send the telemetry event. -+ public isOptedIn = true; -+ public sendErrorTelemetry = true; -+ -+ constructor(private readonly channel: IChannel) {} -+ -+ public publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise { -+ return this.channel.call('publicLog', [eventName, data, anonymizeFilePaths]); -+ } -+ -+ public publicLog2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck, anonymizeFilePaths?: boolean): Promise { -+ return this.channel.call('publicLog2', [eventName, data, anonymizeFilePaths]); -+ } -+ -+ public publicLogError(errorEventName: string, data?: ITelemetryData): Promise { -+ return this.channel.call('publicLogError', [errorEventName, data]); -+ } -+ -+ public publicLogError2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck): Promise { -+ return this.channel.call('publicLogError2', [eventName, data]); -+ } -+ -+ public setEnabled(value: boolean): void { -+ this.channel.call('setEnable', [value]); -+ } -+ -+ public getTelemetryInfo(): Promise { -+ return this.channel.call('getTelemetryInfo'); -+ } -+ -+ public setExperimentProperty(name: string, value: string): void { -+ this.channel.call('setExperimentProperty', [name, value]); -+ } -+} -diff --git a/src/vs/server/entry.ts b/src/vs/server/entry.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..85b306ab07c8d4f204aa6b08766826bf34cf60af ---- /dev/null -+++ b/src/vs/server/entry.ts -@@ -0,0 +1,81 @@ -+import { field } from '@coder/logger'; -+import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; -+import { CodeServerMessage, VscodeMessage } from 'vs/server/ipc'; -+import { logger } from 'vs/server/node/logger'; -+import { enableCustomMarketplace } from 'vs/server/node/marketplace'; -+import { Vscode } from 'vs/server/node/server'; -+import * as proxyAgent from 'vs/base/node/proxy_agent'; -+ -+setUnexpectedErrorHandler((error) => logger.warn(error instanceof Error ? error.message : error)); -+enableCustomMarketplace(); -+proxyAgent.monkeyPatch(true); -+ -+/** -+ * Ensure we control when the process exits. -+ */ -+const exit = process.exit; -+process.exit = function(code?: number) { -+ logger.warn(`process.exit() was prevented: ${code || 'unknown code'}.`); -+} as (code?: number) => never; -+ -+// Kill VS Code if the parent process dies. -+if (typeof process.env.CODE_SERVER_PARENT_PID !== 'undefined') { -+ const parentPid = parseInt(process.env.CODE_SERVER_PARENT_PID, 10); -+ setInterval(() => { -+ try { -+ process.kill(parentPid, 0); // Throws an exception if the process doesn't exist anymore. -+ } catch (e) { -+ exit(); -+ } -+ }, 5000); -+} else { -+ logger.error('no parent process'); -+ exit(1); -+} -+ -+const vscode = new Vscode(); -+const send = (message: VscodeMessage): void => { -+ if (!process.send) { -+ throw new Error('not spawned with IPC'); -+ } -+ process.send(message); -+}; -+ -+// Wait for the init message then start up VS Code. Subsequent messages will -+// return new workbench options without starting a new instance. -+process.on('message', async (message: CodeServerMessage, socket) => { -+ logger.debug('got message from code-server', field('type', message.type)); -+ logger.trace('code-server message content', field('message', message)); -+ switch (message.type) { -+ case 'init': -+ try { -+ const options = await vscode.initialize(message.options); -+ send({ type: 'options', id: message.id, options }); -+ } catch (error) { -+ logger.error(error.message); -+ logger.error(error.stack); -+ exit(1); -+ } -+ break; -+ case 'cli': -+ try { -+ await vscode.cli(message.args); -+ exit(0); -+ } catch (error) { -+ logger.error(error.message); -+ logger.error(error.stack); -+ exit(1); -+ } -+ break; -+ case 'socket': -+ vscode.handleWebSocket(socket, message.query); -+ break; -+ } -+}); -+if (!process.send) { -+ logger.error('not spawned with IPC'); -+ exit(1); -+} else { -+ // This lets the parent know the child is ready to receive messages. -+ send({ type: 'ready' }); -+} -diff --git a/src/vs/server/fork.js b/src/vs/server/fork.js -new file mode 100644 -index 0000000000000000000000000000000000000000..56331ff1fc32bbd82e769aaecb551e427f798ec3 ---- /dev/null -+++ b/src/vs/server/fork.js -@@ -0,0 +1,3 @@ -+// This must be a JS file otherwise when it gets compiled it turns into AMD -+// syntax which will not work without the right loader. -+require('../../bootstrap-amd').load('vs/server/entry'); -diff --git a/src/vs/server/ipc.d.ts b/src/vs/server/ipc.d.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..bc605f03881109ce560c751b550ecaba4671cc74 ---- /dev/null -+++ b/src/vs/server/ipc.d.ts -@@ -0,0 +1,140 @@ -+/** -+ * External interfaces for integration into code-server over IPC. No vs imports -+ * should be made in this file. -+ */ -+export interface Options { -+ base: string -+ disableTelemetry: boolean -+ disableUpdateCheck: boolean -+} -+ -+export interface InitMessage { -+ type: 'init'; -+ id: string; -+ options: VscodeOptions; -+} -+ -+export type Query = { [key: string]: string | string[] | undefined | Query | Query[] }; -+ -+export interface SocketMessage { -+ type: 'socket'; -+ query: Query; -+} -+ -+export interface CliMessage { -+ type: 'cli'; -+ args: Args; -+} -+ -+export interface OpenCommandPipeArgs { -+ type: 'open'; -+ fileURIs?: string[]; -+ folderURIs: string[]; -+ forceNewWindow?: boolean; -+ diffMode?: boolean; -+ addMode?: boolean; -+ gotoLineMode?: boolean; -+ forceReuseWindow?: boolean; -+ waitMarkerFilePath?: string; -+} -+ -+export type CodeServerMessage = InitMessage | SocketMessage | CliMessage; -+ -+export interface ReadyMessage { -+ type: 'ready'; -+} -+ -+export interface OptionsMessage { -+ id: string; -+ type: 'options'; -+ options: WorkbenchOptions; -+} -+ -+export type VscodeMessage = ReadyMessage | OptionsMessage; -+ -+export interface StartPath { -+ url: string; -+ workspace: boolean; -+} -+ -+export interface Args { -+ 'user-data-dir'?: string; -+ -+ 'enable-proposed-api'?: string[]; -+ 'extensions-dir'?: string; -+ 'builtin-extensions-dir'?: string; -+ 'extra-extensions-dir'?: string[]; -+ 'extra-builtin-extensions-dir'?: string[]; -+ 'ignore-last-opened'?: boolean; -+ -+ locale?: string -+ -+ log?: string; -+ verbose?: boolean; -+ home?: string; -+ -+ _: string[]; -+} -+ -+export interface VscodeOptions { -+ readonly args: Args; -+ readonly remoteAuthority: string; -+ readonly startPath?: StartPath; -+} -+ -+export interface VscodeOptionsMessage extends VscodeOptions { -+ readonly id: string; -+} -+ -+export interface UriComponents { -+ readonly scheme: string; -+ readonly authority: string; -+ readonly path: string; -+ readonly query: string; -+ readonly fragment: string; -+} -+ -+export interface NLSConfiguration { -+ locale: string; -+ availableLanguages: { -+ [key: string]: string; -+ }; -+ pseudo?: boolean; -+ _languagePackSupport?: boolean; -+} -+ -+export interface WorkbenchOptions { -+ readonly workbenchWebConfiguration: { -+ readonly remoteAuthority?: string; -+ readonly folderUri?: UriComponents; -+ readonly workspaceUri?: UriComponents; -+ readonly logLevel?: number; -+ readonly workspaceProvider?: { -+ payload: [ -+ ['userDataPath', string], -+ ['enableProposedApi', string], -+ ]; -+ }; -+ readonly homeIndicator?: { -+ href: string, -+ icon: string, -+ title: string, -+ }, -+ }; -+ readonly remoteUserDataUri: UriComponents; -+ readonly productConfiguration: { -+ codeServerVersion?: string; -+ readonly extensionsGallery?: { -+ readonly serviceUrl: string; -+ readonly itemUrl: string; -+ readonly controlUrl: string; -+ readonly recommendationsUrl: string; -+ }; -+ }; -+ readonly nlsConfiguration: NLSConfiguration; -+ readonly commit: string; -+} -+ -+export interface WorkbenchOptionsMessage { -+ id: string; -+} -diff --git a/src/vs/server/node/channel.ts b/src/vs/server/node/channel.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..a6c1f9f848f441b761397ba78e2fef60329b56fa ---- /dev/null -+++ b/src/vs/server/node/channel.ts -@@ -0,0 +1,906 @@ -+import { field, logger } from '@coder/logger'; -+import { Server } from '@coder/node-browser'; -+import * as os from 'os'; -+import * as path from 'path'; -+import { VSBuffer } from 'vs/base/common/buffer'; -+import { CancellationTokenSource } from 'vs/base/common/cancellation'; -+import { Emitter, Event } from 'vs/base/common/event'; -+import { IDisposable } from 'vs/base/common/lifecycle'; -+import * as platform from 'vs/base/common/platform'; -+import * as resources from 'vs/base/common/resources'; -+import { ReadableStreamEventPayload } from 'vs/base/common/stream'; -+import { URI, UriComponents } from 'vs/base/common/uri'; -+import { transformOutgoingURIs } from 'vs/base/common/uriIpc'; -+import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -+import { IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; -+import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -+import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -+import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileReadStreamOptions, FileType, FileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files'; -+import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; -+import { ILogService } from 'vs/platform/log/common/log'; -+import product from 'vs/platform/product/common/product'; -+import { IRemoteAgentEnvironment, RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; -+import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -+import { INodeProxyService } from 'vs/server/common/nodeProxy'; -+import { getTranslations } from 'vs/server/node/nls'; -+import { getUriTransformer } from 'vs/server/node/util'; -+import { IFileChangeDto } from 'vs/workbench/api/common/extHost.protocol'; -+import { IEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -+import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection'; -+import { deserializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; -+import * as terminal from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; -+import { IShellLaunchConfig, ITerminalEnvironment, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; -+import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; -+import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; -+import { getSystemShell } from 'vs/workbench/contrib/terminal/node/terminal'; -+import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; -+import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; -+import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; -+import { ExtensionScanner, ExtensionScannerInput } from 'vs/workbench/services/extensions/node/extensionPoints'; -+ -+/** -+ * Extend the file provider to allow unwatching. -+ */ -+class Watcher extends DiskFileSystemProvider { -+ public readonly watches = new Map(); -+ -+ public dispose(): void { -+ this.watches.forEach((w) => w.dispose()); -+ this.watches.clear(); -+ super.dispose(); -+ } -+ -+ public _watch(req: number, resource: URI, opts: IWatchOptions): void { -+ this.watches.set(req, this.watch(resource, opts)); -+ } -+ -+ public unwatch(req: number): void { -+ this.watches.get(req)!.dispose(); -+ this.watches.delete(req); -+ } -+} -+ -+export class FileProviderChannel implements IServerChannel, IDisposable { -+ private readonly provider: DiskFileSystemProvider; -+ private readonly watchers = new Map(); -+ -+ public constructor( -+ private readonly environmentService: INativeEnvironmentService, -+ private readonly logService: ILogService, -+ ) { -+ this.provider = new DiskFileSystemProvider(this.logService); -+ } -+ -+ public listen(context: RemoteAgentConnectionContext, event: string, args?: any): Event { -+ switch (event) { -+ case 'filechange': return this.filechange(context, args[0]); -+ case 'readFileStream': return this.readFileStream(args[0], args[1]); -+ } -+ -+ throw new Error(`Invalid listen '${event}'`); -+ } -+ -+ private filechange(context: RemoteAgentConnectionContext, session: string): Event { -+ const emitter = new Emitter({ -+ onFirstListenerAdd: () => { -+ const provider = new Watcher(this.logService); -+ this.watchers.set(session, provider); -+ const transformer = getUriTransformer(context.remoteAuthority); -+ provider.onDidChangeFile((events) => { -+ emitter.fire(events.map((event) => ({ -+ ...event, -+ resource: transformer.transformOutgoing(event.resource), -+ }))); -+ }); -+ provider.onDidErrorOccur((event) => this.logService.error(event)); -+ }, -+ onLastListenerRemove: () => { -+ this.watchers.get(session)!.dispose(); -+ this.watchers.delete(session); -+ }, -+ }); -+ -+ return emitter.event; -+ } -+ -+ private readFileStream(resource: UriComponents, opts: FileReadStreamOptions): Event> { -+ const cts = new CancellationTokenSource(); -+ const fileStream = this.provider.readFileStream(this.transform(resource), opts, cts.token); -+ const emitter = new Emitter>({ -+ onFirstListenerAdd: () => { -+ fileStream.on('data', (data) => emitter.fire(VSBuffer.wrap(data))); -+ fileStream.on('error', (error) => emitter.fire(error)); -+ fileStream.on('end', () => emitter.fire('end')); -+ }, -+ onLastListenerRemove: () => cts.cancel(), -+ }); -+ -+ return emitter.event; -+ } -+ -+ public call(_: unknown, command: string, args?: any): Promise { -+ switch (command) { -+ case 'stat': return this.stat(args[0]); -+ case 'open': return this.open(args[0], args[1]); -+ case 'close': return this.close(args[0]); -+ case 'read': return this.read(args[0], args[1], args[2]); -+ case 'readFile': return this.readFile(args[0]); -+ case 'write': return this.write(args[0], args[1], args[2], args[3], args[4]); -+ case 'writeFile': return this.writeFile(args[0], args[1], args[2]); -+ case 'delete': return this.delete(args[0], args[1]); -+ case 'mkdir': return this.mkdir(args[0]); -+ case 'readdir': return this.readdir(args[0]); -+ case 'rename': return this.rename(args[0], args[1], args[2]); -+ case 'copy': return this.copy(args[0], args[1], args[2]); -+ case 'watch': return this.watch(args[0], args[1], args[2], args[3]); -+ case 'unwatch': return this.unwatch(args[0], args[1]); -+ } -+ -+ throw new Error(`Invalid call '${command}'`); -+ } -+ -+ public dispose(): void { -+ this.watchers.forEach((w) => w.dispose()); -+ this.watchers.clear(); -+ } -+ -+ private async stat(resource: UriComponents): Promise { -+ return this.provider.stat(this.transform(resource)); -+ } -+ -+ private async open(resource: UriComponents, opts: FileOpenOptions): Promise { -+ return this.provider.open(this.transform(resource), opts); -+ } -+ -+ private async close(fd: number): Promise { -+ return this.provider.close(fd); -+ } -+ -+ private async read(fd: number, pos: number, length: number): Promise<[VSBuffer, number]> { -+ const buffer = VSBuffer.alloc(length); -+ const bytesRead = await this.provider.read(fd, pos, buffer.buffer, 0, length); -+ return [buffer, bytesRead]; -+ } -+ -+ private async readFile(resource: UriComponents): Promise { -+ return VSBuffer.wrap(await this.provider.readFile(this.transform(resource))); -+ } -+ -+ private write(fd: number, pos: number, buffer: VSBuffer, offset: number, length: number): Promise { -+ return this.provider.write(fd, pos, buffer.buffer, offset, length); -+ } -+ -+ private writeFile(resource: UriComponents, buffer: VSBuffer, opts: FileWriteOptions): Promise { -+ return this.provider.writeFile(this.transform(resource), buffer.buffer, opts); -+ } -+ -+ private async delete(resource: UriComponents, opts: FileDeleteOptions): Promise { -+ return this.provider.delete(this.transform(resource), opts); -+ } -+ -+ private async mkdir(resource: UriComponents): Promise { -+ return this.provider.mkdir(this.transform(resource)); -+ } -+ -+ private async readdir(resource: UriComponents): Promise<[string, FileType][]> { -+ return this.provider.readdir(this.transform(resource)); -+ } -+ -+ private async rename(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise { -+ return this.provider.rename(this.transform(resource), URI.from(target), opts); -+ } -+ -+ private copy(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise { -+ return this.provider.copy(this.transform(resource), URI.from(target), opts); -+ } -+ -+ private async watch(session: string, req: number, resource: UriComponents, opts: IWatchOptions): Promise { -+ this.watchers.get(session)!._watch(req, this.transform(resource), opts); -+ } -+ -+ private async unwatch(session: string, req: number): Promise { -+ this.watchers.get(session)!.unwatch(req); -+ } -+ -+ private transform(resource: UriComponents): URI { -+ // Used for walkthrough content. -+ if (/^\/static[^/]*\//.test(resource.path)) { -+ return URI.file(this.environmentService.appRoot + resource.path.replace(/^\/static[^/]*\//, '/')); -+ // Used by the webview service worker to load resources. -+ } else if (resource.path === '/vscode-resource' && resource.query) { -+ try { -+ const query = JSON.parse(resource.query); -+ if (query.requestResourcePath) { -+ return URI.file(query.requestResourcePath); -+ } -+ } catch (error) { /* Carry on. */ } -+ } -+ return URI.from(resource); -+ } -+} -+ -+// See ../../workbench/services/remote/common/remoteAgentEnvironmentChannel.ts -+export class ExtensionEnvironmentChannel implements IServerChannel { -+ public constructor( -+ private readonly environment: INativeEnvironmentService, -+ private readonly log: ILogService, -+ private readonly telemetry: ITelemetryService, -+ private readonly connectionToken: string, -+ ) {} -+ -+ public listen(_: unknown, event: string): Event { -+ throw new Error(`Invalid listen '${event}'`); -+ } -+ -+ public async call(context: any, command: string, args: any): Promise { -+ switch (command) { -+ case 'getEnvironmentData': -+ return transformOutgoingURIs( -+ await this.getEnvironmentData(), -+ getUriTransformer(context.remoteAuthority), -+ ); -+ case 'scanExtensions': -+ return transformOutgoingURIs( -+ await this.scanExtensions(args.language), -+ getUriTransformer(context.remoteAuthority), -+ ); -+ case 'getDiagnosticInfo': return this.getDiagnosticInfo(); -+ case 'disableTelemetry': return this.disableTelemetry(); -+ case 'logTelemetry': return this.logTelemetry(args[0], args[1]); -+ case 'flushTelemetry': return this.flushTelemetry(); -+ } -+ throw new Error(`Invalid call '${command}'`); -+ } -+ -+ private async getEnvironmentData(): Promise { -+ return { -+ pid: process.pid, -+ connectionToken: this.connectionToken, -+ appRoot: URI.file(this.environment.appRoot), -+ settingsPath: this.environment.settingsResource, -+ logsPath: URI.file(this.environment.logsPath), -+ extensionsPath: URI.file(this.environment.extensionsPath!), -+ extensionHostLogsPath: URI.file(path.join(this.environment.logsPath, 'extension-host')), -+ globalStorageHome: this.environment.globalStorageHome, -+ workspaceStorageHome: this.environment.workspaceStorageHome, -+ userHome: this.environment.userHome, -+ os: platform.OS, -+ }; -+ } -+ -+ private async scanExtensions(language: string): Promise { -+ const translations = await getTranslations(language, this.environment.userDataPath); -+ -+ const scanMultiple = (isBuiltin: boolean, isUnderDevelopment: boolean, paths: string[]): Promise => { -+ return Promise.all(paths.map((path) => { -+ return ExtensionScanner.scanExtensions(new ExtensionScannerInput( -+ product.version, -+ product.commit, -+ language, -+ !!process.env.VSCODE_DEV, -+ path, -+ isBuiltin, -+ isUnderDevelopment, -+ translations, -+ ), this.log); -+ })); -+ }; -+ -+ const scanBuiltin = async (): Promise => { -+ return scanMultiple(true, false, [this.environment.builtinExtensionsPath, ...this.environment.extraBuiltinExtensionPaths]); -+ }; -+ -+ const scanInstalled = async (): Promise => { -+ return scanMultiple(false, true, [this.environment.extensionsPath!, ...this.environment.extraExtensionPaths]); -+ }; -+ -+ return Promise.all([scanBuiltin(), scanInstalled()]).then((allExtensions) => { -+ const uniqueExtensions = new Map(); -+ allExtensions.forEach((multipleExtensions) => { -+ multipleExtensions.forEach((extensions) => { -+ extensions.forEach((extension) => { -+ const id = ExtensionIdentifier.toKey(extension.identifier); -+ if (uniqueExtensions.has(id)) { -+ const oldPath = uniqueExtensions.get(id)!.extensionLocation.fsPath; -+ const newPath = extension.extensionLocation.fsPath; -+ this.log.warn(`${oldPath} has been overridden ${newPath}`); -+ } -+ uniqueExtensions.set(id, { -+ ...extension, -+ // Force extensions that should run on the client due to latency -+ // issues. -+ extensionKind: extension.identifier.value === 'vscodevim.vim' -+ ? [ 'web' ] -+ : extension.extensionKind, -+ }); -+ }); -+ }); -+ }); -+ return Array.from(uniqueExtensions.values()); -+ }); -+ } -+ -+ private getDiagnosticInfo(): Promise { -+ throw new Error('not implemented'); -+ } -+ -+ private async disableTelemetry(): Promise { -+ this.telemetry.setEnabled(false); -+ } -+ -+ private async logTelemetry(eventName: string, data: ITelemetryData): Promise { -+ this.telemetry.publicLog(eventName, data); -+ } -+ -+ private async flushTelemetry(): Promise { -+ // We always send immediately at the moment. -+ } -+} -+ -+export class NodeProxyService implements INodeProxyService { -+ public _serviceBrand = undefined; -+ -+ public readonly server: Server; -+ -+ private readonly _onMessage = new Emitter(); -+ public readonly onMessage = this._onMessage.event; -+ private readonly _$onMessage = new Emitter(); -+ public readonly $onMessage = this._$onMessage.event; -+ public readonly _onDown = new Emitter(); -+ public readonly onDown = this._onDown.event; -+ public readonly _onUp = new Emitter(); -+ public readonly onUp = this._onUp.event; -+ -+ // Unused because the server connection will never permanently close. -+ private readonly _onClose = new Emitter(); -+ public readonly onClose = this._onClose.event; -+ -+ public constructor() { -+ // TODO: down/up -+ this.server = new Server({ -+ onMessage: this.$onMessage, -+ onClose: this.onClose, -+ onDown: this.onDown, -+ onUp: this.onUp, -+ send: (message: string): void => { -+ this._onMessage.fire(message); -+ } -+ }); -+ } -+ -+ public send(message: string): void { -+ this._$onMessage.fire(message); -+ } -+} -+ -+class VariableResolverService extends AbstractVariableResolverService { -+ constructor( -+ remoteAuthority: string, -+ args: terminal.ICreateTerminalProcessArguments, -+ env: platform.IProcessEnvironment, -+ ) { -+ super({ -+ getFolderUri: (name: string): URI | undefined => { -+ const folder = args.workspaceFolders.find((f) => f.name === name); -+ return folder && URI.revive(folder.uri); -+ }, -+ getWorkspaceFolderCount: (): number => { -+ return args.workspaceFolders.length; -+ }, -+ // In ../../workbench/contrib/terminal/common/remoteTerminalChannel.ts it -+ // looks like there are `config:` entries which must be for this? Not sure -+ // how/if the URI comes into play though. -+ getConfigurationValue: (_: URI, section: string): string | undefined => { -+ return args.resolvedVariables[`config:${section}`]; -+ }, -+ getExecPath: (): string | undefined => { -+ // Assuming that resolverEnv is just for use in the resolver and not for -+ // the terminal itself. -+ return (args.resolverEnv && args.resolverEnv['VSCODE_EXEC_PATH']) || env['VSCODE_EXEC_PATH']; -+ }, -+ // This is just a guess; this is the only file-related thing we're sent -+ // and none of these resolver methods seem to get called so I don't know -+ // how to test. -+ getFilePath: (): string | undefined => { -+ const resource = transformIncoming(remoteAuthority, args.activeFileResource); -+ if (!resource) { -+ return undefined; -+ } -+ // See ../../editor/standalone/browser/simpleServices.ts; -+ // `BaseConfigurationResolverService` calls `getUriLabel` from there. -+ if (resource.scheme === 'file') { -+ return resource.fsPath; -+ } -+ return resource.path; -+ }, -+ // It looks like these are set here although they aren't on the types: -+ // ../../workbench/contrib/terminal/common/remoteTerminalChannel.ts -+ getSelectedText: (): string | undefined => { -+ return args.resolvedVariables.selectedText; -+ }, -+ getLineNumber: (): string | undefined => { -+ return args.resolvedVariables.selectedText; -+ }, -+ }, undefined, env); -+ } -+} -+ -+class Terminal { -+ private readonly process: TerminalProcess; -+ private _pid: number = -1; -+ private _title: string = ''; -+ public readonly workspaceId: string; -+ public readonly workspaceName: string; -+ private readonly persist: boolean; -+ -+ private readonly _onDispose = new Emitter(); -+ public get onDispose(): Event { return this._onDispose.event; } -+ -+ private _isOrphan = true; -+ public get isOrphan(): boolean { return this._isOrphan; } -+ -+ // These are replayed when a client reconnects. -+ private cols: number; -+ private rows: number; -+ private replayData: string[] = []; -+ // This is based on string length and is pretty arbitrary. -+ private readonly maxReplayData = 10000; -+ private totalReplayData = 0; -+ -+ // According to the release notes the terminals are supposed to dispose after -+ // a short timeout; in our case we'll use 48 hours so you can get them back -+ // the next day or over the weekend. -+ private disposeTimeout: NodeJS.Timeout | undefined; -+ private disposeDelay = 48 * 60 * 60 * 1000; -+ -+ private buffering = false; -+ private readonly _onEvent = new Emitter({ -+ // Don't bind to data until something is listening. -+ onFirstListenerAdd: () => { -+ logger.debug('Terminal bound', field('id', this.id)); -+ this._isOrphan = false; -+ if (!this.buffering) { -+ this.buffering = true; -+ this.bufferer.startBuffering(this.id, this.process.onProcessData); -+ } -+ }, -+ -+ // Replay stored events. -+ onFirstListenerDidAdd: () => { -+ // We only need to replay if the terminal is being reconnected which is -+ // true if there is a dispose timeout. -+ if (typeof this.disposeTimeout !== 'undefined') { -+ return; -+ } -+ -+ clearTimeout(this.disposeTimeout); -+ this.disposeTimeout = undefined; -+ -+ logger.debug('Terminal replaying', field('id', this.id)); -+ this._onEvent.fire({ -+ type: 'replay', -+ events: [{ -+ cols: this.cols, -+ rows: this.rows, -+ data: this.replayData.join(''), -+ }] -+ }); -+ }, -+ -+ onLastListenerRemove: () => { -+ logger.debug('Terminal unbound', field('id', this.id)); -+ this._isOrphan = true; -+ if (!this.persist) { // Used by debug consoles. -+ this.dispose(); -+ } else { -+ this.disposeTimeout = setTimeout(() => { -+ this.dispose(); -+ }, this.disposeDelay); -+ } -+ } -+ }); -+ -+ public get onEvent(): Event { return this._onEvent.event; } -+ -+ // Buffer to reduce the number of messages going to the renderer. -+ private readonly bufferer = new TerminalDataBufferer((_, data) => { -+ this._onEvent.fire({ -+ type: 'data', -+ data, -+ }); -+ -+ // No need to store data if we aren't persisting. -+ if (!this.persist) { -+ return; -+ } -+ -+ this.replayData.push(data); -+ this.totalReplayData += data.length; -+ -+ let overflow = this.totalReplayData - this.maxReplayData; -+ if (overflow <= 0) { -+ return; -+ } -+ -+ // Drop events until doing so would put us under budget. -+ let deleteCount = 0; -+ for (; deleteCount < this.replayData.length -+ && this.replayData[deleteCount].length <= overflow; ++deleteCount) { -+ overflow -= this.replayData[deleteCount].length; -+ } -+ -+ if (deleteCount > 0) { -+ this.replayData.splice(0, deleteCount); -+ } -+ -+ // Dropping any more events would put us under budget; trim the first event -+ // instead if still over budget. -+ if (overflow > 0 && this.replayData.length > 0) { -+ this.replayData[0] = this.replayData[0].substring(overflow); -+ } -+ -+ this.totalReplayData = this.replayData.reduce((p, c) => p + c.length, 0); -+ }); -+ -+ public get pid(): number { -+ return this._pid; -+ } -+ -+ public get title(): string { -+ return this._title; -+ } -+ -+ public constructor( -+ public readonly id: number, -+ config: IShellLaunchConfig & { cwd: string }, -+ args: terminal.ICreateTerminalProcessArguments, -+ env: platform.IProcessEnvironment, -+ logService: ILogService, -+ ) { -+ this.workspaceId = args.workspaceId; -+ this.workspaceName = args.workspaceName; -+ -+ this.cols = args.cols; -+ this.rows = args.rows; -+ -+ // TODO: Don't persist terminals until we make it work with things like -+ // htop, vim, etc. -+ // this.persist = args.shouldPersistTerminal; -+ this.persist = false; -+ -+ this.process = new TerminalProcess( -+ config, -+ config.cwd, -+ this.cols, -+ this.rows, -+ env, -+ process.env as platform.IProcessEnvironment, // Environment used for `findExecutable`. -+ false, // windowsEnableConpty: boolean, -+ logService, -+ ); -+ -+ // The current pid and title aren't exposed so they have to be tracked. -+ this.process.onProcessReady((event) => { -+ this._pid = event.pid; -+ this._onEvent.fire({ -+ type: 'ready', -+ pid: event.pid, -+ cwd: event.cwd, -+ }); -+ }); -+ -+ this.process.onProcessTitleChanged((title) => { -+ this._title = title; -+ this._onEvent.fire({ -+ type: 'titleChanged', -+ title, -+ }); -+ }); -+ -+ this.process.onProcessExit((exitCode) => { -+ logger.debug('Terminal exited', field('id', this.id), field('code', exitCode)); -+ this._onEvent.fire({ -+ type: 'exit', -+ exitCode, -+ }); -+ this.dispose(); -+ }); -+ -+ // TODO: I think `execCommand` must have something to do with running -+ // commands on the terminal that will do things in VS Code but we already -+ // have that functionality via a socket so I'm not sure what this is for. -+ // type: 'execCommand'; -+ // reqId: number; -+ // commandId: string; -+ // commandArgs: any[]; -+ -+ // TODO: Maybe this is to ask if the terminal is currently attached to -+ // anything? But we already know that on account of whether anything is -+ // listening to our event emitter. -+ // type: 'orphan?'; -+ } -+ -+ public dispose() { -+ logger.debug('Terminal disposing', field('id', this.id)); -+ this._onEvent.dispose(); -+ this.bufferer.dispose(); -+ this.process.dispose(); -+ this.process.shutdown(true); -+ this._onDispose.fire(); -+ this._onDispose.dispose(); -+ } -+ -+ public shutdown(immediate: boolean): void { -+ return this.process.shutdown(immediate); -+ } -+ -+ public getCwd(): Promise { -+ return this.process.getCwd(); -+ } -+ -+ public getInitialCwd(): Promise { -+ return this.process.getInitialCwd(); -+ } -+ -+ public start(): Promise { -+ return this.process.start(); -+ } -+ -+ public input(data: string): void { -+ return this.process.input(data); -+ } -+ -+ public resize(cols: number, rows: number): void { -+ this.cols = cols; -+ this.rows = rows; -+ return this.process.resize(cols, rows); -+ } -+} -+ -+// References: - ../../workbench/api/node/extHostTerminalService.ts -+// - ../../workbench/contrib/terminal/browser/terminalProcessManager.ts -+export class TerminalProviderChannel implements IServerChannel, IDisposable { -+ private readonly terminals = new Map(); -+ private id = 0; -+ -+ public constructor (private readonly logService: ILogService) { -+ -+ } -+ -+ public listen(_: RemoteAgentConnectionContext, event: string, args?: any): Event { -+ switch (event) { -+ case '$onTerminalProcessEvent': return this.onTerminalProcessEvent(args); -+ } -+ -+ throw new Error(`Invalid listen '${event}'`); -+ } -+ -+ private onTerminalProcessEvent(args: terminal.IOnTerminalProcessEventArguments): Event { -+ return this.getTerminal(args.id).onEvent; -+ } -+ -+ public call(context: RemoteAgentConnectionContext, command: string, args?: any): Promise { -+ switch (command) { -+ case '$createTerminalProcess': return this.createTerminalProcess(context.remoteAuthority, args); -+ case '$startTerminalProcess': return this.startTerminalProcess(args); -+ case '$sendInputToTerminalProcess': return this.sendInputToTerminalProcess(args); -+ case '$shutdownTerminalProcess': return this.shutdownTerminalProcess(args); -+ case '$resizeTerminalProcess': return this.resizeTerminalProcess(args); -+ case '$getTerminalInitialCwd': return this.getTerminalInitialCwd(args); -+ case '$getTerminalCwd': return this.getTerminalCwd(args); -+ case '$sendCommandResultToTerminalProcess': return this.sendCommandResultToTerminalProcess(args); -+ case '$orphanQuestionReply': return this.orphanQuestionReply(args[0]); -+ case '$listTerminals': return this.listTerminals(args[0]); -+ } -+ -+ throw new Error(`Invalid call '${command}'`); -+ } -+ -+ public dispose(): void { -+ this.terminals.forEach((t) => t.dispose()); -+ } -+ -+ private async createTerminalProcess(remoteAuthority: string, args: terminal.ICreateTerminalProcessArguments): Promise { -+ const terminalId = this.id++; -+ logger.debug('Creating terminal', field('id', terminalId), field('terminals', this.terminals.size)); -+ -+ const shellLaunchConfig: IShellLaunchConfig = { -+ name: args.shellLaunchConfig.name, -+ executable: args.shellLaunchConfig.executable, -+ args: args.shellLaunchConfig.args, -+ // TODO: Should we transform if it's a string as well? The incoming -+ // transform only takes `UriComponents` so I suspect it's not necessary. -+ cwd: typeof args.shellLaunchConfig.cwd !== 'string' -+ ? transformIncoming(remoteAuthority, args.shellLaunchConfig.cwd) -+ : args.shellLaunchConfig.cwd, -+ env: args.shellLaunchConfig.env, -+ }; -+ -+ const activeWorkspaceUri = transformIncoming(remoteAuthority, args.activeWorkspaceFolder?.uri); -+ const activeWorkspace = activeWorkspaceUri && args.activeWorkspaceFolder ? { -+ ...args.activeWorkspaceFolder, -+ uri: activeWorkspaceUri, -+ toResource: (relativePath: string) => resources.joinPath(activeWorkspaceUri, relativePath), -+ } : undefined; -+ -+ const resolverService = new VariableResolverService(remoteAuthority, args, process.env as platform.IProcessEnvironment); -+ const resolver = terminalEnvironment.createVariableResolver(activeWorkspace, resolverService); -+ -+ const getDefaultShellAndArgs = (): { executable: string; args: string[] | string } => { -+ if (shellLaunchConfig.executable) { -+ const executable = resolverService.resolve(activeWorkspace, shellLaunchConfig.executable); -+ let resolvedArgs: string[] | string = []; -+ if (shellLaunchConfig.args && Array.isArray(shellLaunchConfig.args)) { -+ for (const arg of shellLaunchConfig.args) { -+ resolvedArgs.push(resolverService.resolve(activeWorkspace, arg)); -+ } -+ } else if (shellLaunchConfig.args) { -+ resolvedArgs = resolverService.resolve(activeWorkspace, shellLaunchConfig.args); -+ } -+ return { executable, args: resolvedArgs }; -+ } -+ -+ const executable = terminalEnvironment.getDefaultShell( -+ (key) => args.configuration[key], -+ args.isWorkspaceShellAllowed, -+ getSystemShell(platform.platform), -+ process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'), -+ process.env.windir, -+ resolver, -+ this.logService, -+ false, // useAutomationShell -+ ); -+ -+ const resolvedArgs = terminalEnvironment.getDefaultShellArgs( -+ (key) => args.configuration[key], -+ args.isWorkspaceShellAllowed, -+ false, // useAutomationShell -+ resolver, -+ this.logService, -+ ); -+ -+ return { executable, args: resolvedArgs }; -+ }; -+ -+ const getInitialCwd = (): string => { -+ return terminalEnvironment.getCwd( -+ shellLaunchConfig, -+ os.homedir(), -+ resolver, -+ activeWorkspaceUri, -+ args.configuration['terminal.integrated.cwd'], -+ this.logService, -+ ); -+ }; -+ -+ // Use a separate var so Typescript recognizes these properties are no -+ // longer undefined. -+ const resolvedShellLaunchConfig = { -+ ...shellLaunchConfig, -+ ...getDefaultShellAndArgs(), -+ cwd: getInitialCwd(), -+ }; -+ -+ logger.debug('Resolved shell launch configuration', field('id', terminalId)); -+ -+ // Use instead of `terminal.integrated.env.${platform}` to make types work. -+ const getEnvFromConfig = (): terminal.ISingleTerminalConfiguration => { -+ if (platform.isWindows) { -+ return args.configuration['terminal.integrated.env.windows']; -+ } else if (platform.isMacintosh) { -+ return args.configuration['terminal.integrated.env.osx']; -+ } -+ return args.configuration['terminal.integrated.env.linux']; -+ }; -+ -+ const getNonInheritedEnv = async (): Promise => { -+ const env = await getMainProcessParentEnv(); -+ env.VSCODE_IPC_HOOK_CLI = process.env['VSCODE_IPC_HOOK_CLI']!; -+ return env; -+ }; -+ -+ const env = terminalEnvironment.createTerminalEnvironment( -+ shellLaunchConfig, -+ getEnvFromConfig(), -+ resolver, -+ args.isWorkspaceShellAllowed, -+ product.version, -+ args.configuration['terminal.integrated.detectLocale'], -+ args.configuration['terminal.integrated.inheritEnv'] !== false -+ ? process.env as platform.IProcessEnvironment -+ : await getNonInheritedEnv() -+ ); -+ -+ // Apply extension environment variable collections to the environment. -+ if (!shellLaunchConfig.strictEnv) { -+ // They come in an array and in serialized format. -+ const envVariableCollections = new Map(); -+ for (const [k, v] of args.envVariableCollections) { -+ envVariableCollections.set(k, { map: deserializeEnvironmentVariableCollection(v) }); -+ } -+ const mergedCollection = new MergedEnvironmentVariableCollection(envVariableCollections); -+ mergedCollection.applyToProcessEnvironment(env); -+ } -+ -+ logger.debug('Resolved terminal environment', field('id', terminalId)); -+ -+ const terminal = new Terminal(terminalId, resolvedShellLaunchConfig, args, env, this.logService); -+ this.terminals.set(terminalId, terminal); -+ logger.debug('Created terminal', field('id', terminalId)); -+ terminal.onDispose(() => this.terminals.delete(terminalId)); -+ -+ return { -+ terminalId, -+ resolvedShellLaunchConfig, -+ }; -+ } -+ -+ private getTerminal(id: number): Terminal { -+ const terminal = this.terminals.get(id); -+ if (!terminal) { -+ throw new Error(`terminal with id ${id} does not exist`); -+ } -+ return terminal; -+ } -+ -+ private async startTerminalProcess(args: terminal.IStartTerminalProcessArguments): Promise { -+ return this.getTerminal(args.id).start(); -+ } -+ -+ private async sendInputToTerminalProcess(args: terminal.ISendInputToTerminalProcessArguments): Promise { -+ return this.getTerminal(args.id).input(args.data); -+ } -+ -+ private async shutdownTerminalProcess(args: terminal.IShutdownTerminalProcessArguments): Promise { -+ return this.getTerminal(args.id).shutdown(args.immediate); -+ } -+ -+ private async resizeTerminalProcess(args: terminal.IResizeTerminalProcessArguments): Promise { -+ return this.getTerminal(args.id).resize(args.cols, args.rows); -+ } -+ -+ private async getTerminalInitialCwd(args: terminal.IGetTerminalInitialCwdArguments): Promise { -+ return this.getTerminal(args.id).getInitialCwd(); -+ } -+ -+ private async getTerminalCwd(args: terminal.IGetTerminalCwdArguments): Promise { -+ return this.getTerminal(args.id).getCwd(); -+ } -+ -+ private async sendCommandResultToTerminalProcess(_: terminal.ISendCommandResultToTerminalProcessArguments): Promise { -+ // NOTE: Not required unless we implement the `execCommand` event, see above. -+ throw new Error('not implemented'); -+ } -+ -+ private async orphanQuestionReply(_: terminal.IOrphanQuestionReplyArgs): Promise { -+ // NOTE: Not required unless we implement the `orphan?` event, see above. -+ throw new Error('not implemented'); -+ } -+ -+ private async listTerminals(_: terminal.IListTerminalsArgs): Promise { -+ // TODO: args.isInitialization. Maybe this is to have slightly different -+ // behavior when first listing terminals but I don't know what you'd want to -+ // do differently. Maybe it's to reset the terminal dispose timeouts or -+ // something like that, but why not do it each time you list? -+ const terminals = await Promise.all(Array.from(this.terminals).map(async ([id, terminal]) => { -+ const cwd = await terminal.getCwd(); -+ return { -+ id, -+ pid: terminal.pid, -+ title: terminal.title, -+ cwd, -+ workspaceId: terminal.workspaceId, -+ workspaceName: terminal.workspaceName, -+ isOrphan: terminal.isOrphan, -+ }; -+ })); -+ // Only returned orphaned terminals so we don't end up attaching to -+ // terminals already attached elsewhere. -+ return terminals.filter((t) => t.isOrphan); -+ } -+} -+ -+function transformIncoming(remoteAuthority: string, uri: UriComponents | undefined): URI | undefined { -+ const transformer = getUriTransformer(remoteAuthority); -+ return uri ? URI.revive(transformer.transformIncoming(uri)) : uri; -+} -diff --git a/src/vs/server/node/connection.ts b/src/vs/server/node/connection.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..6b7c5ffd2ef2f321e32b6ebf1e6ad93b9a99c80f ---- /dev/null -+++ b/src/vs/server/node/connection.ts -@@ -0,0 +1,192 @@ -+import { field, Logger, logger } from '@coder/logger'; -+import * as cp from 'child_process'; -+import { VSBuffer } from 'vs/base/common/buffer'; -+import { Emitter } from 'vs/base/common/event'; -+import { FileAccess } from 'vs/base/common/network'; -+import { ISocket } from 'vs/base/parts/ipc/common/ipc.net'; -+import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; -+import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -+import { getNlsConfiguration } from 'vs/server/node/nls'; -+import { Protocol } from 'vs/server/node/protocol'; -+import { IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; -+ -+export abstract class Connection { -+ private readonly _onClose = new Emitter(); -+ public readonly onClose = this._onClose.event; -+ private disposed = false; -+ private _offline: number | undefined; -+ -+ public constructor(protected protocol: Protocol, public readonly token: string) {} -+ -+ public get offline(): number | undefined { -+ return this._offline; -+ } -+ -+ public reconnect(socket: ISocket, buffer: VSBuffer): void { -+ this._offline = undefined; -+ this.doReconnect(socket, buffer); -+ } -+ -+ public dispose(): void { -+ if (!this.disposed) { -+ this.disposed = true; -+ this.doDispose(); -+ this._onClose.fire(); -+ } -+ } -+ -+ protected setOffline(): void { -+ if (!this._offline) { -+ this._offline = Date.now(); -+ } -+ } -+ -+ /** -+ * Set up the connection on a new socket. -+ */ -+ protected abstract doReconnect(socket: ISocket, buffer: VSBuffer): void; -+ protected abstract doDispose(): void; -+} -+ -+/** -+ * Used for all the IPC channels. -+ */ -+export class ManagementConnection extends Connection { -+ public constructor(protected protocol: Protocol, token: string) { -+ super(protocol, token); -+ protocol.onClose(() => this.dispose()); // Explicit close. -+ protocol.onSocketClose(() => this.setOffline()); // Might reconnect. -+ } -+ -+ protected doDispose(): void { -+ this.protocol.sendDisconnect(); -+ this.protocol.dispose(); -+ this.protocol.getUnderlyingSocket().destroy(); -+ } -+ -+ protected doReconnect(socket: ISocket, buffer: VSBuffer): void { -+ this.protocol.beginAcceptReconnection(socket, buffer); -+ this.protocol.endAcceptReconnection(); -+ } -+} -+ -+interface DisconnectedMessage { -+ type: 'VSCODE_EXTHOST_DISCONNECTED'; -+} -+ -+interface ConsoleMessage { -+ type: '__$console'; -+ // See bootstrap-fork.js#L135. -+ severity: 'log' | 'warn' | 'error'; -+ arguments: any[]; -+} -+ -+type ExtHostMessage = DisconnectedMessage | ConsoleMessage | IExtHostReadyMessage; -+ -+export class ExtensionHostConnection extends Connection { -+ private process?: cp.ChildProcess; -+ private readonly logger: Logger; -+ -+ public constructor( -+ locale:string, protocol: Protocol, buffer: VSBuffer, token: string, -+ private readonly environment: INativeEnvironmentService, -+ ) { -+ super(protocol, token); -+ this.logger = logger.named('exthost', field('token', token)); -+ this.protocol.dispose(); -+ this.spawn(locale, buffer).then((p) => this.process = p); -+ this.protocol.getUnderlyingSocket().pause(); -+ } -+ -+ protected doDispose(): void { -+ if (this.process) { -+ this.process.kill(); -+ } -+ this.protocol.getUnderlyingSocket().destroy(); -+ } -+ -+ protected doReconnect(socket: ISocket, buffer: VSBuffer): void { -+ // This is just to set the new socket. -+ this.protocol.beginAcceptReconnection(socket, null); -+ this.protocol.dispose(); -+ this.sendInitMessage(buffer); -+ } -+ -+ private sendInitMessage(buffer: VSBuffer): void { -+ const socket = this.protocol.getUnderlyingSocket(); -+ socket.pause(); -+ this.logger.trace('Sending socket'); -+ this.process!.send({ // Process must be set at this point. -+ type: 'VSCODE_EXTHOST_IPC_SOCKET', -+ initialDataChunk: (buffer.buffer as Buffer).toString('base64'), -+ skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket, -+ }, socket); -+ } -+ -+ private async spawn(locale: string, buffer: VSBuffer): Promise { -+ this.logger.trace('Getting NLS configuration...'); -+ const config = await getNlsConfiguration(locale, this.environment.userDataPath); -+ this.logger.trace('Spawning extension host...'); -+ const proc = cp.fork( -+ FileAccess.asFileUri('bootstrap-fork', require).fsPath, -+ // While not technically necessary, makes it easier to tell which process -+ // bootstrap-fork is executing. Can also do pkill -f extensionHost -+ // Other spawns in the VS Code codebase behave similarly. -+ [ '--type=extensionHost' ], -+ { -+ env: { -+ ...process.env, -+ AMD_ENTRYPOINT: 'vs/workbench/services/extensions/node/extensionHostProcess', -+ PIPE_LOGGING: 'true', -+ VERBOSE_LOGGING: 'true', -+ VSCODE_EXTHOST_WILL_SEND_SOCKET: 'true', -+ VSCODE_HANDLES_UNCAUGHT_ERRORS: 'true', -+ VSCODE_LOG_STACK: 'false', -+ VSCODE_LOG_LEVEL: process.env.LOG_LEVEL, -+ VSCODE_NLS_CONFIG: JSON.stringify(config), -+ }, -+ silent: true, -+ }, -+ ); -+ -+ proc.on('error', (error) => { -+ this.logger.error('Exited unexpectedly', field('error', error)); -+ this.dispose(); -+ }); -+ proc.on('exit', (code) => { -+ this.logger.trace('Exited', field('code', code)); -+ this.dispose(); -+ }); -+ if (proc.stdout && proc.stderr) { -+ proc.stdout.setEncoding('utf8').on('data', (d) => this.logger.info(d)); -+ proc.stderr.setEncoding('utf8').on('data', (d) => this.logger.error(d)); -+ } -+ -+ proc.on('message', (event: ExtHostMessage) => { -+ switch (event.type) { -+ case '__$console': -+ const fn = this.logger[event.severity === 'log' ? 'info' : event.severity]; -+ if (fn) { -+ fn.bind(this.logger)('console', field('arguments', event.arguments)); -+ } else { -+ this.logger.error('Unexpected severity', field('event', event)); -+ } -+ break; -+ case 'VSCODE_EXTHOST_DISCONNECTED': -+ this.logger.trace('Going offline'); -+ this.setOffline(); -+ break; -+ case 'VSCODE_EXTHOST_IPC_READY': -+ this.logger.trace('Got ready message'); -+ this.sendInitMessage(buffer); -+ break; -+ default: -+ this.logger.error('Unexpected message', field('event', event)); -+ break; -+ } -+ }); -+ -+ this.logger.trace('Waiting for handshake...'); -+ return proc; -+ } -+} -diff --git a/src/vs/server/node/insights.ts b/src/vs/server/node/insights.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..a0ece345f28f06afb2af12fe4901ad228b2475a4 ---- /dev/null -+++ b/src/vs/server/node/insights.ts -@@ -0,0 +1,124 @@ -+import * as appInsights from 'applicationinsights'; -+import * as https from 'https'; -+import * as http from 'http'; -+import * as os from 'os'; -+ -+class Channel { -+ public get _sender() { -+ throw new Error('unimplemented'); -+ } -+ public get _buffer() { -+ throw new Error('unimplemented'); -+ } -+ -+ public setUseDiskRetryCaching(): void { -+ throw new Error('unimplemented'); -+ } -+ public send(): void { -+ throw new Error('unimplemented'); -+ } -+ public triggerSend(): void { -+ throw new Error('unimplemented'); -+ } -+} -+ -+export class TelemetryClient { -+ public context: any = undefined; -+ public commonProperties: any = undefined; -+ public config: any = {}; -+ -+ public channel: any = new Channel(); -+ -+ public addTelemetryProcessor(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public clearTelemetryProcessors(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public runTelemetryProcessors(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public trackTrace(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public trackMetric(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public trackException(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public trackRequest(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public trackDependency(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public track(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public trackNodeHttpRequestSync(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public trackNodeHttpRequest(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public trackNodeHttpDependency(): void { -+ throw new Error('unimplemented'); -+ } -+ -+ public trackEvent(options: appInsights.Contracts.EventTelemetry): void { -+ if (!options.properties) { -+ options.properties = {}; -+ } -+ if (!options.measurements) { -+ options.measurements = {}; -+ } -+ -+ try { -+ const cpus = os.cpus(); -+ options.measurements.cores = cpus.length; -+ options.properties['common.cpuModel'] = cpus[0].model; -+ } catch (error) {} -+ -+ try { -+ options.measurements.memoryFree = os.freemem(); -+ options.measurements.memoryTotal = os.totalmem(); -+ } catch (error) {} -+ -+ try { -+ options.properties['common.shell'] = os.userInfo().shell; -+ options.properties['common.release'] = os.release(); -+ options.properties['common.arch'] = os.arch(); -+ } catch (error) {} -+ -+ try { -+ const url = process.env.TELEMETRY_URL || 'https://v1.telemetry.coder.com/track'; -+ const request = (/^http:/.test(url) ? http : https).request(url, { -+ method: 'POST', -+ headers: { -+ 'Content-Type': 'application/json', -+ }, -+ }); -+ request.on('error', () => { /* We don't care. */ }); -+ request.write(JSON.stringify(options)); -+ request.end(); -+ } catch (error) {} -+ } -+ -+ public flush(options: { callback: (v: string) => void }): void { -+ if (options.callback) { -+ options.callback(''); -+ } -+ } -+} -diff --git a/src/vs/server/node/ipc.ts b/src/vs/server/node/ipc.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..5e560eb46e6a0a18c91e440c655ac0d44b09b6dd ---- /dev/null -+++ b/src/vs/server/node/ipc.ts -@@ -0,0 +1,61 @@ -+import * as cp from 'child_process'; -+import { Emitter } from 'vs/base/common/event'; -+ -+enum ControlMessage { -+ okToChild = 'ok>', -+ okFromChild = 'ok<', -+} -+ -+interface RelaunchMessage { -+ type: 'relaunch'; -+ version: string; -+} -+ -+export type Message = RelaunchMessage; -+ -+class IpcMain { -+ protected readonly _onMessage = new Emitter(); -+ public readonly onMessage = this._onMessage.event; -+ -+ public handshake(child?: cp.ChildProcess): Promise { -+ return new Promise((resolve, reject) => { -+ const target = child || process; -+ if (!target.send) { -+ throw new Error('Not spawned with IPC enabled'); -+ } -+ target.on('message', (message) => { -+ if (message === child ? ControlMessage.okFromChild : ControlMessage.okToChild) { -+ target.removeAllListeners(); -+ target.on('message', (msg) => this._onMessage.fire(msg)); -+ if (child) { -+ target.send!(ControlMessage.okToChild); -+ } -+ resolve(); -+ } -+ }); -+ if (child) { -+ child.once('error', reject); -+ child.once('exit', (code) => { -+ const error = new Error(`Unexpected exit with code ${code}`); -+ (error as any).code = code; -+ reject(error); -+ }); -+ } else { -+ target.send(ControlMessage.okFromChild); -+ } -+ }); -+ } -+ -+ public relaunch(version: string): void { -+ this.send({ type: 'relaunch', version }); -+ } -+ -+ private send(message: Message): void { -+ if (!process.send) { -+ throw new Error('Not a child process with IPC enabled'); -+ } -+ process.send(message); -+ } -+} -+ -+export const ipcMain = new IpcMain(); -diff --git a/src/vs/server/node/logger.ts b/src/vs/server/node/logger.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..2a39c524aaa1b4031e04a631842f30b6fec3d98a ---- /dev/null -+++ b/src/vs/server/node/logger.ts -@@ -0,0 +1,2 @@ -+import { logger as baseLogger } from '@coder/logger'; -+export const logger = baseLogger.named('vscode'); -diff --git a/src/vs/server/node/marketplace.ts b/src/vs/server/node/marketplace.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..8956fc40d48448b9932036c4c286464881807338 ---- /dev/null -+++ b/src/vs/server/node/marketplace.ts -@@ -0,0 +1,174 @@ -+import * as fs from 'fs'; -+import * as path from 'path'; -+import * as tarStream from 'tar-stream'; -+import * as util from 'util'; -+import { CancellationToken } from 'vs/base/common/cancellation'; -+import { mkdirp } from 'vs/base/node/pfs'; -+import * as vszip from 'vs/base/node/zip'; -+import * as nls from 'vs/nls'; -+import product from 'vs/platform/product/common/product'; -+ -+// We will be overriding these, so keep a reference to the original. -+const vszipExtract = vszip.extract; -+const vszipBuffer = vszip.buffer; -+ -+export interface IExtractOptions { -+ overwrite?: boolean; -+ /** -+ * Source path within the TAR/ZIP archive. Only the files -+ * contained in this path will be extracted. -+ */ -+ sourcePath?: string; -+} -+ -+export interface IFile { -+ path: string; -+ contents?: Buffer | string; -+ localPath?: string; -+} -+ -+export const tar = async (tarPath: string, files: IFile[]): Promise => { -+ const pack = tarStream.pack(); -+ const chunks: Buffer[] = []; -+ const ended = new Promise((resolve) => { -+ pack.on('end', () => resolve(Buffer.concat(chunks))); -+ }); -+ pack.on('data', (chunk: Buffer) => chunks.push(chunk)); -+ for (let i = 0; i < files.length; i++) { -+ const file = files[i]; -+ pack.entry({ name: file.path }, file.contents); -+ } -+ pack.finalize(); -+ await util.promisify(fs.writeFile)(tarPath, await ended); -+ return tarPath; -+}; -+ -+export const extract = async (archivePath: string, extractPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise => { -+ try { -+ await extractTar(archivePath, extractPath, options, token); -+ } catch (error) { -+ if (error.toString().includes('Invalid tar header')) { -+ await vszipExtract(archivePath, extractPath, options, token); -+ } -+ } -+}; -+ -+export const buffer = (targetPath: string, filePath: string): Promise => { -+ return new Promise(async (resolve, reject) => { -+ try { -+ let done: boolean = false; -+ await extractAssets(targetPath, new RegExp(filePath), (assetPath: string, data: Buffer) => { -+ if (path.normalize(assetPath) === path.normalize(filePath)) { -+ done = true; -+ resolve(data); -+ } -+ }); -+ if (!done) { -+ throw new Error('couldn\'t find asset ' + filePath); -+ } -+ } catch (error) { -+ if (error.toString().includes('Invalid tar header')) { -+ vszipBuffer(targetPath, filePath).then(resolve).catch(reject); -+ } else { -+ reject(error); -+ } -+ } -+ }); -+}; -+ -+const extractAssets = async (tarPath: string, match: RegExp, callback: (path: string, data: Buffer) => void): Promise => { -+ return new Promise((resolve, reject): void => { -+ const extractor = tarStream.extract(); -+ const fail = (error: Error) => { -+ extractor.destroy(); -+ reject(error); -+ }; -+ extractor.once('error', fail); -+ extractor.on('entry', async (header, stream, next) => { -+ const name = header.name; -+ if (match.test(name)) { -+ extractData(stream).then((data) => { -+ callback(name, data); -+ next(); -+ }).catch(fail); -+ } else { -+ stream.on('end', () => next()); -+ stream.resume(); // Just drain it. -+ } -+ }); -+ extractor.on('finish', resolve); -+ fs.createReadStream(tarPath).pipe(extractor); -+ }); -+}; -+ -+const extractData = (stream: NodeJS.ReadableStream): Promise => { -+ return new Promise((resolve, reject): void => { -+ const fileData: Buffer[] = []; -+ stream.on('error', reject); -+ stream.on('end', () => resolve(Buffer.concat(fileData))); -+ stream.on('data', (data) => fileData.push(data)); -+ }); -+}; -+ -+const extractTar = async (tarPath: string, targetPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise => { -+ return new Promise((resolve, reject): void => { -+ const sourcePathRegex = new RegExp(options.sourcePath ? `^${options.sourcePath}` : ''); -+ const extractor = tarStream.extract(); -+ const fail = (error: Error) => { -+ extractor.destroy(); -+ reject(error); -+ }; -+ extractor.once('error', fail); -+ extractor.on('entry', async (header, stream, next) => { -+ const nextEntry = (): void => { -+ stream.on('end', () => next()); -+ stream.resume(); -+ }; -+ -+ const rawName = path.normalize(header.name); -+ if (token.isCancellationRequested || !sourcePathRegex.test(rawName)) { -+ return nextEntry(); -+ } -+ -+ const fileName = rawName.replace(sourcePathRegex, ''); -+ const targetFileName = path.join(targetPath, fileName); -+ if (/\/$/.test(fileName)) { -+ return mkdirp(targetFileName).then(nextEntry); -+ } -+ -+ const dirName = path.dirname(fileName); -+ const targetDirName = path.join(targetPath, dirName); -+ if (targetDirName.indexOf(targetPath) !== 0) { -+ return fail(new Error(nls.localize('invalid file', 'Error extracting {0}. Invalid file.', fileName))); -+ } -+ -+ await mkdirp(targetDirName, undefined); -+ -+ const fstream = fs.createWriteStream(targetFileName, { mode: header.mode }); -+ fstream.once('close', () => next()); -+ fstream.once('error', fail); -+ stream.pipe(fstream); -+ }); -+ extractor.once('finish', resolve); -+ fs.createReadStream(tarPath).pipe(extractor); -+ }); -+}; -+ -+/** -+ * Override original functionality so we can use a custom marketplace with -+ * either tars or zips. -+ */ -+export const enableCustomMarketplace = (): void => { -+ (product).extensionsGallery = { // Use `any` to override readonly. -+ serviceUrl: process.env.SERVICE_URL || 'https://extensions.coder.com/api', -+ itemUrl: process.env.ITEM_URL || '', -+ controlUrl: '', -+ recommendationsUrl: '', -+ ...(product.extensionsGallery || {}), -+ }; -+ -+ const target = vszip as typeof vszip; -+ target.zip = tar; -+ target.extract = extract; -+ target.buffer = buffer; -+}; -diff --git a/src/vs/server/node/nls.ts b/src/vs/server/node/nls.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..3d428a57d31f29c40f9c3ce45f715b443badf4e9 ---- /dev/null -+++ b/src/vs/server/node/nls.ts -@@ -0,0 +1,88 @@ -+import * as fs from 'fs'; -+import * as path from 'path'; -+import * as util from 'util'; -+import { getPathFromAmdModule } from 'vs/base/common/amd'; -+import * as lp from 'vs/base/node/languagePacks'; -+import product from 'vs/platform/product/common/product'; -+import { Translations } from 'vs/workbench/services/extensions/common/extensionPoints'; -+ -+const configurations = new Map>(); -+const metadataPath = path.join(getPathFromAmdModule(require, ''), 'nls.metadata.json'); -+ -+export const isInternalConfiguration = (config: lp.NLSConfiguration): config is lp.InternalNLSConfiguration => { -+ return config && !!(config)._languagePackId; -+}; -+ -+const DefaultConfiguration = { -+ locale: 'en', -+ availableLanguages: {}, -+}; -+ -+export const getNlsConfiguration = async (locale: string, userDataPath: string): Promise => { -+ const id = `${locale}: ${userDataPath}`; -+ if (!configurations.has(id)) { -+ configurations.set(id, new Promise(async (resolve) => { -+ const config = product.commit && await util.promisify(fs.exists)(metadataPath) -+ ? await lp.getNLSConfiguration(product.commit, userDataPath, metadataPath, locale) -+ : DefaultConfiguration; -+ if (isInternalConfiguration(config)) { -+ config._languagePackSupport = true; -+ } -+ // If the configuration has no results keep trying since code-server -+ // doesn't restart when a language is installed so this result would -+ // persist (the plugin might not be installed yet or something). -+ if (config.locale !== 'en' && config.locale !== 'en-us' && Object.keys(config.availableLanguages).length === 0) { -+ configurations.delete(id); -+ } -+ resolve(config); -+ })); -+ } -+ return configurations.get(id)!; -+}; -+ -+export const getTranslations = async (locale: string, userDataPath: string): Promise => { -+ const config = await getNlsConfiguration(locale, userDataPath); -+ if (isInternalConfiguration(config)) { -+ try { -+ return JSON.parse(await util.promisify(fs.readFile)(config._translationsConfigFile, 'utf8')); -+ } catch (error) { /* Nothing yet. */} -+ } -+ return {}; -+}; -+ -+export const getLocaleFromConfig = async (userDataPath: string): Promise => { -+ const files = ['locale.json', 'argv.json']; -+ for (let i = 0; i < files.length; ++i) { -+ try { -+ const localeConfigUri = path.join(userDataPath, 'User', files[i]); -+ const content = stripComments(await util.promisify(fs.readFile)(localeConfigUri, 'utf8')); -+ return JSON.parse(content).locale; -+ } catch (error) { /* Ignore. */ } -+ } -+ return 'en'; -+}; -+ -+// Taken from src/main.js in the main VS Code source. -+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; -+ } -+ }); -+}; -diff --git a/src/vs/server/node/protocol.ts b/src/vs/server/node/protocol.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..57213f92828fafefcab0e3c401a1e8ede472c9cc ---- /dev/null -+++ b/src/vs/server/node/protocol.ts -@@ -0,0 +1,91 @@ -+import { field } from '@coder/logger'; -+import * as net from 'net'; -+import { VSBuffer } from 'vs/base/common/buffer'; -+import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; -+import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; -+import { AuthRequest, ConnectionTypeRequest, HandshakeMessage } from 'vs/platform/remote/common/remoteAgentConnection'; -+import { logger } from 'vs/server/node/logger'; -+ -+export interface SocketOptions { -+ readonly reconnectionToken: string; -+ readonly reconnection: boolean; -+ readonly skipWebSocketFrames: boolean; -+} -+ -+export class Protocol extends PersistentProtocol { -+ public constructor(socket: net.Socket, public readonly options: SocketOptions) { -+ super( -+ options.skipWebSocketFrames -+ ? new NodeSocket(socket) -+ : new WebSocketNodeSocket(new NodeSocket(socket)), -+ ); -+ } -+ -+ public getUnderlyingSocket(): net.Socket { -+ const socket = this.getSocket(); -+ return socket instanceof NodeSocket -+ ? socket.socket -+ : (socket as WebSocketNodeSocket).socket.socket; -+ } -+ -+ /** -+ * Perform a handshake to get a connection request. -+ */ -+ public handshake(): Promise { -+ logger.trace('Protocol handshake', field('token', this.options.reconnectionToken)); -+ return new Promise((resolve, reject) => { -+ const timeout = setTimeout(() => { -+ logger.error('Handshake timed out', field('token', this.options.reconnectionToken)); -+ reject(new Error('timed out')); -+ }, 10000); // Matches the client timeout. -+ -+ const handler = this.onControlMessage((rawMessage) => { -+ try { -+ const raw = rawMessage.toString(); -+ logger.trace('Protocol message', field('token', this.options.reconnectionToken), field('message', raw)); -+ const message = JSON.parse(raw); -+ switch (message.type) { -+ case 'auth': -+ return this.authenticate(message); -+ case 'connectionType': -+ handler.dispose(); -+ clearTimeout(timeout); -+ return resolve(message); -+ default: -+ throw new Error('Unrecognized message type'); -+ } -+ } catch (error) { -+ handler.dispose(); -+ clearTimeout(timeout); -+ reject(error); -+ } -+ }); -+ -+ // Kick off the handshake in case we missed the client's opening shot. -+ // TODO: Investigate why that message seems to get lost. -+ this.authenticate(); -+ }); -+ } -+ -+ /** -+ * TODO: This ignores the authentication process entirely for now. -+ */ -+ private authenticate(_?: AuthRequest): void { -+ this.sendMessage({ type: 'sign', data: '' }); -+ } -+ -+ /** -+ * TODO: implement. -+ */ -+ public tunnel(): void { -+ throw new Error('Tunnel is not implemented yet'); -+ } -+ -+ /** -+ * Send a handshake message. In the case of the extension host, it just sends -+ * back a debug port. -+ */ -+ public sendMessage(message: HandshakeMessage | { debugPort?: number } ): void { -+ this.sendControl(VSBuffer.fromString(JSON.stringify(message))); -+ } -+} -diff --git a/src/vs/server/node/server.ts b/src/vs/server/node/server.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..0f4d31a961be1c055163b0cf29d550d7af5146e6 ---- /dev/null -+++ b/src/vs/server/node/server.ts -@@ -0,0 +1,308 @@ -+import { field } from '@coder/logger'; -+import * as fs from 'fs'; -+import * as net from 'net'; -+import * as path from 'path'; -+import { Emitter } from 'vs/base/common/event'; -+import { Schemas } from 'vs/base/common/network'; -+import { URI } from 'vs/base/common/uri'; -+import { getMachineId } from 'vs/base/node/id'; -+import { ClientConnectionEvent, createChannelReceiver, IPCServer, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -+import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; -+import { main } from 'vs/code/node/cliProcessMain'; -+import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -+import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; -+import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; -+import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; -+import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -+import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; -+import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; -+import { IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -+import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -+import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; -+import { IFileService } from 'vs/platform/files/common/files'; -+import { FileService } from 'vs/platform/files/common/fileService'; -+import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; -+import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -+import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -+import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -+import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -+import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; -+import { getLogLevel, ILoggerService, ILogService } from 'vs/platform/log/common/log'; -+import { LoggerChannel } from 'vs/platform/log/common/logIpc'; -+import { LoggerService } from 'vs/platform/log/node/loggerService'; -+import { SpdLogService } from 'vs/platform/log/node/spdlogService'; -+import product from 'vs/platform/product/common/product'; -+import { IProductService } from 'vs/platform/product/common/productService'; -+import { ConnectionType, ConnectionTypeRequest } from 'vs/platform/remote/common/remoteAgentConnection'; -+import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; -+import { IRequestService } from 'vs/platform/request/common/request'; -+import { RequestChannel } from 'vs/platform/request/common/requestIpc'; -+import { RequestService } from 'vs/platform/request/node/requestService'; -+import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; -+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -+import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender'; -+import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -+import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -+import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; -+import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -+import { INodeProxyService, NodeProxyChannel } from 'vs/server/common/nodeProxy'; -+import { TelemetryChannel } from 'vs/server/common/telemetry'; -+import { Query, VscodeOptions, WorkbenchOptions } from 'vs/server/ipc'; -+import { ExtensionEnvironmentChannel, FileProviderChannel, NodeProxyService, TerminalProviderChannel } from 'vs/server/node/channel'; -+import { Connection, ExtensionHostConnection, ManagementConnection } from 'vs/server/node/connection'; -+import { TelemetryClient } from 'vs/server/node/insights'; -+import { logger } from 'vs/server/node/logger'; -+import { getLocaleFromConfig, getNlsConfiguration } from 'vs/server/node/nls'; -+import { Protocol } from 'vs/server/node/protocol'; -+import { getUriTransformer } from 'vs/server/node/util'; -+import { REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; -+import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel'; -+import { RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; -+import { localize } from 'vs/nls'; -+ -+export class Vscode { -+ public readonly _onDidClientConnect = new Emitter(); -+ public readonly onDidClientConnect = this._onDidClientConnect.event; -+ private readonly ipc = new IPCServer(this.onDidClientConnect); -+ -+ private readonly maxExtraOfflineConnections = 0; -+ private readonly connections = new Map>(); -+ -+ private readonly services = new ServiceCollection(); -+ private servicesPromise?: Promise; -+ -+ public async cli(args: NativeParsedArgs): Promise { -+ return main(args); -+ } -+ -+ public async initialize(options: VscodeOptions): Promise { -+ const transformer = getUriTransformer(options.remoteAuthority); -+ if (!this.servicesPromise) { -+ this.servicesPromise = this.initializeServices(options.args); -+ } -+ await this.servicesPromise; -+ const environment = this.services.get(IEnvironmentService) as INativeEnvironmentService; -+ const startPath = options.startPath; -+ const parseUrl = (url: string): URI => { -+ // This might be a fully-specified URL or just a path. -+ try { -+ return URI.parse(url, true); -+ } catch (error) { -+ return URI.from({ -+ scheme: Schemas.vscodeRemote, -+ authority: options.remoteAuthority, -+ path: url, -+ }); -+ } -+ }; -+ return { -+ workbenchWebConfiguration: { -+ workspaceUri: startPath && startPath.workspace ? parseUrl(startPath.url) : undefined, -+ folderUri: startPath && !startPath.workspace ? parseUrl(startPath.url) : undefined, -+ remoteAuthority: options.remoteAuthority, -+ logLevel: getLogLevel(environment), -+ workspaceProvider: { -+ payload: [ -+ ['userDataPath', environment.userDataPath], -+ ['enableProposedApi', JSON.stringify(options.args['enable-proposed-api'] || [])] -+ ], -+ }, -+ homeIndicator: { -+ href: options.args.home || 'https://github.com/cdr/code-server', -+ icon: 'code', -+ title: localize('home', "Home"), -+ }, -+ }, -+ remoteUserDataUri: transformer.transformOutgoing(URI.file(environment.userDataPath)), -+ productConfiguration: product, -+ nlsConfiguration: await getNlsConfiguration(environment.args.locale || await getLocaleFromConfig(environment.userDataPath), environment.userDataPath), -+ commit: product.commit || 'development', -+ }; -+ } -+ -+ public async handleWebSocket(socket: net.Socket, query: Query): Promise { -+ if (!query.reconnectionToken) { -+ throw new Error('Reconnection token is missing from query parameters'); -+ } -+ const protocol = new Protocol(socket, { -+ reconnectionToken: query.reconnectionToken, -+ reconnection: query.reconnection === 'true', -+ skipWebSocketFrames: query.skipWebSocketFrames === 'true', -+ }); -+ try { -+ await this.connect(await protocol.handshake(), protocol); -+ } catch (error) { -+ protocol.sendMessage({ type: 'error', reason: error.message }); -+ protocol.dispose(); -+ protocol.getSocket().dispose(); -+ } -+ return true; -+ } -+ -+ private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise { -+ if (product.commit && message.commit !== product.commit) { -+ logger.warn(`Version mismatch (${message.commit} instead of ${product.commit})`); -+ } -+ -+ switch (message.desiredConnectionType) { -+ case ConnectionType.ExtensionHost: -+ case ConnectionType.Management: -+ if (!this.connections.has(message.desiredConnectionType)) { -+ this.connections.set(message.desiredConnectionType, new Map()); -+ } -+ const connections = this.connections.get(message.desiredConnectionType)!; -+ -+ const ok = async () => { -+ return message.desiredConnectionType === ConnectionType.ExtensionHost -+ ? { debugPort: await this.getDebugPort() } -+ : { type: 'ok' }; -+ }; -+ -+ const token = protocol.options.reconnectionToken; -+ if (protocol.options.reconnection && connections.has(token)) { -+ protocol.sendMessage(await ok()); -+ const buffer = protocol.readEntireBuffer(); -+ protocol.dispose(); -+ return connections.get(token)!.reconnect(protocol.getSocket(), buffer); -+ } else if (protocol.options.reconnection || connections.has(token)) { -+ throw new Error(protocol.options.reconnection -+ ? 'Unrecognized reconnection token' -+ : 'Duplicate reconnection token' -+ ); -+ } -+ -+ logger.debug('New connection', field('token', token)); -+ protocol.sendMessage(await ok()); -+ -+ let connection: Connection; -+ if (message.desiredConnectionType === ConnectionType.Management) { -+ connection = new ManagementConnection(protocol, token); -+ this._onDidClientConnect.fire({ -+ protocol, onDidClientDisconnect: connection.onClose, -+ }); -+ // TODO: Need a way to match clients with a connection. For now -+ // dispose everything which only works because no extensions currently -+ // utilize long-running proxies. -+ (this.services.get(INodeProxyService) as NodeProxyService)._onUp.fire(); -+ connection.onClose(() => (this.services.get(INodeProxyService) as NodeProxyService)._onDown.fire()); -+ } else { -+ const buffer = protocol.readEntireBuffer(); -+ connection = new ExtensionHostConnection( -+ message.args ? message.args.language : 'en', -+ protocol, buffer, token, -+ this.services.get(IEnvironmentService) as INativeEnvironmentService, -+ ); -+ } -+ connections.set(token, connection); -+ connection.onClose(() => { -+ logger.debug('Connection closed', field('token', token)); -+ connections.delete(token); -+ }); -+ this.disposeOldOfflineConnections(connections); -+ break; -+ case ConnectionType.Tunnel: return protocol.tunnel(); -+ default: throw new Error('Unrecognized connection type'); -+ } -+ } -+ -+ private disposeOldOfflineConnections(connections: Map): void { -+ const offline = Array.from(connections.values()) -+ .filter((connection) => typeof connection.offline !== 'undefined'); -+ for (let i = 0, max = offline.length - this.maxExtraOfflineConnections; i < max; ++i) { -+ logger.debug('Disposing offline connection', field('token', offline[i].token)); -+ offline[i].dispose(); -+ } -+ } -+ -+ private async initializeServices(args: NativeParsedArgs): Promise { -+ const environmentService = new NativeEnvironmentService(args); -+ // https://github.com/cdr/code-server/issues/1693 -+ fs.mkdirSync(environmentService.globalStorageHome.fsPath, { recursive: true }); -+ -+ const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService)); -+ const fileService = new FileService(logService); -+ fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService)); -+ -+ const piiPaths = [ -+ path.join(environmentService.userDataPath, 'clp'), // Language packs. -+ environmentService.appRoot, -+ environmentService.extensionsPath, -+ environmentService.builtinExtensionsPath, -+ ...environmentService.extraExtensionPaths, -+ ...environmentService.extraBuiltinExtensionPaths, -+ ]; -+ -+ this.ipc.registerChannel('logger', new LoggerChannel(logService)); -+ this.ipc.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel()); -+ -+ this.services.set(ILogService, logService); -+ this.services.set(IEnvironmentService, environmentService); -+ this.services.set(INativeEnvironmentService, environmentService); -+ this.services.set(ILoggerService, new SyncDescriptor(LoggerService)); -+ -+ const configurationService = new ConfigurationService(environmentService.settingsResource, fileService); -+ await configurationService.initialize(); -+ this.services.set(IConfigurationService, configurationService); -+ -+ this.services.set(IRequestService, new SyncDescriptor(RequestService)); -+ this.services.set(IFileService, fileService); -+ this.services.set(IProductService, { _serviceBrand: undefined, ...product }); -+ -+ const machineId = await getMachineId(); -+ -+ await new Promise((resolve) => { -+ const instantiationService = new InstantiationService(this.services); -+ -+ instantiationService.invokeFunction((accessor) => { -+ instantiationService.createInstance(LogsDataCleaner); -+ -+ let telemetryService: ITelemetryService; -+ if (!environmentService.disableTelemetry) { -+ telemetryService = new TelemetryService({ -+ appender: combinedAppender( -+ new AppInsightsAppender('code-server', null, () => new TelemetryClient() as any), -+ new TelemetryLogAppender(accessor.get(ILoggerService), environmentService) -+ ), -+ sendErrorTelemetry: true, -+ commonProperties: resolveCommonProperties( -+ product.commit, product.version, machineId, -+ [], environmentService.installSourcePath, 'code-server', -+ ), -+ piiPaths, -+ }, configurationService); -+ } else { -+ telemetryService = NullTelemetryService; -+ } -+ -+ this.services.set(ITelemetryService, telemetryService); -+ -+ this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); -+ this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); -+ this.services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); -+ this.services.set(INodeProxyService, new SyncDescriptor(NodeProxyService)); -+ -+ this.ipc.registerChannel('extensions', new ExtensionManagementChannel( -+ accessor.get(IExtensionManagementService), -+ (context) => getUriTransformer(context.remoteAuthority), -+ )); -+ this.ipc.registerChannel('remoteextensionsenvironment', new ExtensionEnvironmentChannel( -+ environmentService, logService, telemetryService, '', -+ )); -+ this.ipc.registerChannel('request', new RequestChannel(accessor.get(IRequestService))); -+ this.ipc.registerChannel('telemetry', new TelemetryChannel(telemetryService)); -+ this.ipc.registerChannel('nodeProxy', new NodeProxyChannel(accessor.get(INodeProxyService))); -+ this.ipc.registerChannel('localizations', >createChannelReceiver(accessor.get(ILocalizationsService))); -+ this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService)); -+ this.ipc.registerChannel(REMOTE_TERMINAL_CHANNEL_NAME, new TerminalProviderChannel(logService)); -+ resolve(new ErrorTelemetry(telemetryService)); -+ }); -+ }); -+ } -+ -+ /** -+ * TODO: implement. -+ */ -+ private async getDebugPort(): Promise { -+ return undefined; -+ } -+} -diff --git a/src/vs/server/node/util.ts b/src/vs/server/node/util.ts -new file mode 100644 -index 0000000000000000000000000000000000000000..d76f655e36647b1c9d38d2f7986f2b78a4bcfb50 ---- /dev/null -+++ b/src/vs/server/node/util.ts -@@ -0,0 +1,13 @@ -+import { URITransformer } from 'vs/base/common/uriIpc'; -+ -+export const getUriTransformer = (remoteAuthority: string): URITransformer => { -+ return new URITransformer(remoteAuthority); -+}; -+ -+/** -+ * Encode a path for opening via the folder or workspace query parameter. This -+ * preserves slashes so it can be edited by hand more easily. -+ */ -+export const encodePath = (path: string): string => { -+ return path.split('/').map((p) => encodeURIComponent(p)).join('/'); -+}; -diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts -index a4df8523631563a498c9ab6e51105074616a481a..f03da094e9080544102bbd3f037a71b348e5bd83 100644 ---- a/src/vs/workbench/api/browser/extensionHost.contribution.ts -+++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts -@@ -61,6 +61,7 @@ import './mainThreadComments'; - import './mainThreadNotebook'; - import './mainThreadTask'; - import './mainThreadLabelService'; -+import 'vs/server/browser/mainThreadNodeProxy'; - import './mainThreadTunnelService'; - import './mainThreadAuthentication'; - import './mainThreadTimeline'; -diff --git a/src/vs/workbench/api/browser/mainThreadStorage.ts b/src/vs/workbench/api/browser/mainThreadStorage.ts -index 57abf0e86a5edeeb2bc497af5e140ec13d9b5810..704d0f9ae19d436a7207ff735aabc289c422dd1e 100644 ---- a/src/vs/workbench/api/browser/mainThreadStorage.ts -+++ b/src/vs/workbench/api/browser/mainThreadStorage.ts -@@ -62,11 +62,11 @@ export class MainThreadStorage implements MainThreadStorageShape { - return JSON.parse(jsonValue); - } - -- $setValue(shared: boolean, key: string, value: object): Promise { -+ async $setValue(shared: boolean, key: string, value: object): Promise { - let jsonValue: string; - try { - jsonValue = JSON.stringify(value); -- this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE); -+ await this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE); - } catch (err) { - return Promise.reject(err); - } -diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts -index 284c6aff854a747d1202c34581a1419c35e9654f..f0173d80103ca91b5eab144a10935bc0990119c9 100644 ---- a/src/vs/workbench/api/common/extHost.api.impl.ts -+++ b/src/vs/workbench/api/common/extHost.api.impl.ts -@@ -68,6 +68,7 @@ import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransf - import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; - import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; - import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; -+import { IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy'; - import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming'; - import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; - import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; -@@ -103,6 +104,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I - const extHostStorage = accessor.get(IExtHostStorage); - const extensionStoragePaths = accessor.get(IExtensionStoragePaths); - const extHostLogService = accessor.get(ILogService); -+ const extHostNodeProxy = accessor.get(IExtHostNodeProxy); - const extHostTunnelService = accessor.get(IExtHostTunnelService); - const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService); - const extHostWindow = accessor.get(IExtHostWindow); -@@ -114,6 +116,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I - rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration); - rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService); - rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage); -+ rpcProtocol.set(ExtHostContext.ExtHostNodeProxy, extHostNodeProxy); - rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService); - rpcProtocol.set(ExtHostContext.ExtHostWindow, extHostWindow); - -diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts -index 77ef6577821399b150407e980c8fd35e9d005ca6..264e3361accec20e4e1eaae10ae8ca05e47b1fae 100644 ---- a/src/vs/workbench/api/common/extHost.protocol.ts -+++ b/src/vs/workbench/api/common/extHost.protocol.ts -@@ -816,6 +816,17 @@ export interface MainThreadLabelServiceShape extends IDisposable { - $unregisterResourceLabelFormatter(handle: number): void; - } - -+export interface MainThreadNodeProxyShape extends IDisposable { -+ $send(message: string): void; -+ $fetchExtension(extensionUri: UriComponents): Promise; -+} -+export interface ExtHostNodeProxyShape { -+ $onMessage(message: string): void; -+ $onClose(): void; -+ $onDown(): void; -+ $onUp(): void; -+} -+ - export interface MainThreadSearchShape extends IDisposable { - $registerFileSearchProvider(handle: number, scheme: string): void; - $registerTextSearchProvider(handle: number, scheme: string): void; -@@ -1796,6 +1807,7 @@ export const MainContext = { - MainThreadWindow: createMainId('MainThreadWindow'), - MainThreadLabelService: createMainId('MainThreadLabelService'), - MainThreadNotebook: createMainId('MainThreadNotebook'), -+ MainThreadNodeProxy: createMainId('MainThreadNodeProxy'), - MainThreadTheming: createMainId('MainThreadTheming'), - MainThreadTunnelService: createMainId('MainThreadTunnelService'), - MainThreadTimeline: createMainId('MainThreadTimeline') -@@ -1838,6 +1850,7 @@ export const ExtHostContext = { - ExtHostOutputService: createMainId('ExtHostOutputService'), - ExtHosLabelService: createMainId('ExtHostLabelService'), - ExtHostNotebook: createMainId('ExtHostNotebook'), -+ ExtHostNodeProxy: createMainId('ExtHostNodeProxy'), - ExtHostTheming: createMainId('ExtHostTheming'), - ExtHostTunnelService: createMainId('ExtHostTunnelService'), - ExtHostAuthentication: createMainId('ExtHostAuthentication'), -diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts -index 328b9327207e4f2068bfab6cf374c622d8c5fc69..38963843095c9116011665027f46d3fb85c30ff8 100644 ---- a/src/vs/workbench/api/common/extHostExtensionService.ts -+++ b/src/vs/workbench/api/common/extHostExtensionService.ts -@@ -31,6 +31,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData - import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; - import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; - import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -+import { IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy'; - import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; - import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; - import { Emitter, Event } from 'vs/base/common/event'; -@@ -82,6 +83,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme - protected readonly _extHostWorkspace: ExtHostWorkspace; - protected readonly _extHostConfiguration: ExtHostConfiguration; - protected readonly _logService: ILogService; -+ protected readonly _nodeProxy: IExtHostNodeProxy; - protected readonly _extHostTunnelService: IExtHostTunnelService; - protected readonly _extHostTerminalService: IExtHostTerminalService; - -@@ -114,6 +116,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme - @ILogService logService: ILogService, - @IExtHostInitDataService initData: IExtHostInitDataService, - @IExtensionStoragePaths storagePath: IExtensionStoragePaths, -+ @IExtHostNodeProxy nodeProxy: IExtHostNodeProxy, - @IExtHostTunnelService extHostTunnelService: IExtHostTunnelService, - @IExtHostTerminalService extHostTerminalService: IExtHostTerminalService - ) { -@@ -125,6 +128,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme - this._extHostWorkspace = extHostWorkspace; - this._extHostConfiguration = extHostConfiguration; - this._logService = logService; -+ this._nodeProxy = nodeProxy; - this._extHostTunnelService = extHostTunnelService; - this._extHostTerminalService = extHostTerminalService; - this._disposables = new DisposableStore(); -@@ -362,7 +366,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme - - const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); - return Promise.all([ -- this._loadCommonJSModule(joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder), -+ this._loadCommonJSModule(joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder, !extensionDescription.browser), - this._loadExtensionContext(extensionDescription) - ]).then(values => { - return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); -@@ -754,7 +758,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme - - protected abstract _beforeAlmostReadyToRunExtensions(): Promise; - protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined; -- protected abstract _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; -+ protected abstract _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder, isRemote?: boolean): Promise; - public abstract $setRemoteEnvironment(env: { [key: string]: string | null }): Promise; - } - -diff --git a/src/vs/workbench/api/node/extHost.node.services.ts b/src/vs/workbench/api/node/extHost.node.services.ts -index b3c89e51cfc25a53293a352a2a8ad50d5f26d595..e21abe4e13bc25a5b72f556bbfb61085842faeb7 100644 ---- a/src/vs/workbench/api/node/extHost.node.services.ts -+++ b/src/vs/workbench/api/node/extHost.node.services.ts -@@ -3,6 +3,8 @@ - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -+import { IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy'; -+import { NotImplementedProxy } from 'vs/base/common/types'; - import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; - import { ExtHostOutputService2 } from 'vs/workbench/api/node/extHostOutputService'; - import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService'; -@@ -36,3 +38,4 @@ registerSingleton(IExtHostSearch, NativeExtHostSearch); - registerSingleton(IExtHostTask, ExtHostTask); - registerSingleton(IExtHostTerminalService, ExtHostTerminalService); - registerSingleton(IExtHostTunnelService, ExtHostTunnelService); -+registerSingleton(IExtHostNodeProxy, class extends NotImplementedProxy(String(IExtHostNodeProxy)) { whenReady = Promise.resolve(); }); -diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts -index b3857616f7006127c423dcef7020ae4653da5ff6..594cd75b546a4b845e56122c846f63e29dc5f948 100644 ---- a/src/vs/workbench/api/node/extHostCLIServer.ts -+++ b/src/vs/workbench/api/node/extHostCLIServer.ts -@@ -11,6 +11,8 @@ import { IWindowOpenable, IOpenWindowOptions } from 'vs/platform/windows/common/ - import { URI } from 'vs/base/common/uri'; - import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; - import { ILogService } from 'vs/platform/log/common/log'; -+import { join } from 'vs/base/common/path'; -+import { tmpdir } from 'os'; - - export interface OpenCommandPipeArgs { - type: 'open'; -@@ -58,6 +60,11 @@ export class CLIServerBase { - } - - private async setup(): Promise { -+ // NOTE@coder: Write this out so we can get the most recent path. -+ fs.promises.writeFile(join(tmpdir(), 'vscode-ipc'), this._ipcHandlePath).catch((error) => { -+ this.logService.error(error); -+ }); -+ - try { - this._server.listen(this.ipcHandlePath); - this._server.on('error', err => this.logService.error(err)); -diff --git a/src/vs/workbench/api/worker/extHost.worker.services.ts b/src/vs/workbench/api/worker/extHost.worker.services.ts -index 3843fdec386edc09a1d361b63de892a04e0070ed..8aac4df527857e964798362a69f5591bef07c165 100644 ---- a/src/vs/workbench/api/worker/extHost.worker.services.ts -+++ b/src/vs/workbench/api/worker/extHost.worker.services.ts -@@ -8,6 +8,7 @@ import { ILogService } from 'vs/platform/log/common/log'; - import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; - import { ExtHostExtensionService } from 'vs/workbench/api/worker/extHostExtensionService'; - import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService'; -+import { ExtHostNodeProxy, IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy'; - - // ######################################################################### - // ### ### -@@ -17,3 +18,4 @@ import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService'; - - registerSingleton(IExtHostExtensionService, ExtHostExtensionService); - registerSingleton(ILogService, ExtHostLogService); -+registerSingleton(IExtHostNodeProxy, ExtHostNodeProxy); -diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts -index 021af6e0f8983c492f9cdd048ba2dcae7640bc1d..4474a93beba03365709c3cda98b682131ad4745f 100644 ---- a/src/vs/workbench/api/worker/extHostExtensionService.ts -+++ b/src/vs/workbench/api/worker/extHostExtensionService.ts -@@ -11,6 +11,7 @@ import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterc - import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; - import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes'; - import { timeout } from 'vs/base/common/async'; -+import { loadCommonJSModule } from 'vs/server/browser/worker'; - - class WorkerRequireInterceptor extends RequireInterceptor { - -@@ -46,10 +47,15 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { - } - - protected _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined { -- return extensionDescription.browser; -+ // NOTE@coder: We can support regular Node modules as well. These will just -+ // require the root of the extension. -+ return extensionDescription.browser || '.'; - } - -- protected async _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { -+ protected async _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder, isRemote?: boolean): Promise { -+ if (isRemote) { -+ return loadCommonJSModule(module, activationTimesBuilder, this._nodeProxy, this._logService, this._fakeModules!.getModule('vscode', module)); -+ } - - module = module.with({ path: ensureSuffix(module.path, '.js') }); - const response = await fetch(module.toString(true)); -diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts -index 7344a3a29b32f7b370b99bf0cfdc79a322195ff8..46c900da7610d9ca6ddff4556b5d93b8dd58588c 100644 ---- a/src/vs/workbench/browser/actions/navigationActions.ts -+++ b/src/vs/workbench/browser/actions/navigationActions.ts -@@ -311,3 +311,6 @@ actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousP - - const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); - workbenchRegistry.registerWorkbenchContribution(GoHomeContributor, LifecyclePhase.Ready); -+export const _1 = workbenchRegistry; -+export const _2 = GoHomeContributor; -+export const _3 = LifecyclePhase.Ready; -diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css -index ced2d815834e40a1543e80516472799075980733..dfcae73e8a042307600c67f163aa00ba9e0762f4 100644 ---- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css -+++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css -@@ -55,6 +55,10 @@ - align-items: center; - justify-content: center; - order: -1; -+ -+ /* NOTE@coder: Hide since it doesn't seem to do anything when used with -+ code-server except open the VS Code repository. */ -+ display: none !important; - } - - .monaco-workbench .activitybar > .content > .home-bar > .home-bar-icon-badge { -diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts -index 80544aab34c12bb42a36519885e9872ef2b24158..17b56856a0b3fd936dbc094ff39797d5b8ccaadf 100644 ---- a/src/vs/workbench/browser/web.main.ts -+++ b/src/vs/workbench/browser/web.main.ts -@@ -43,6 +43,7 @@ import { FileLogService } from 'vs/platform/log/common/fileLogService'; - import { toLocalISOString } from 'vs/base/common/date'; - import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows'; - import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; -+import { initialize } from 'vs/server/browser/client'; - import { coalesce } from 'vs/base/common/arrays'; - import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; - import { ICommandService } from 'vs/platform/commands/common/commands'; -@@ -101,6 +102,8 @@ class BrowserMain extends Disposable { - // Startup - const instantiationService = workbench.startup(); - -+ await initialize(services.serviceCollection); -+ - // Return API Facade - return instantiationService.invokeFunction(accessor => { - const commandService = accessor.get(ICommandService); -diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts -index 94e7e7a4bac154c45078a1b5034e50634a7a43af..8164200dcef1efbc65b50eef9c270af3ca655fbd 100644 ---- a/src/vs/workbench/common/resources.ts -+++ b/src/vs/workbench/common/resources.ts -@@ -15,6 +15,7 @@ import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; - import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; - import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; - import { withNullAsUndefined } from 'vs/base/common/types'; -+import { Schemas } from 'vs/base/common/network'; - - export class ResourceContextKey extends Disposable implements IContextKey { - -@@ -74,7 +75,8 @@ export class ResourceContextKey extends Disposable implements IContextKey { - if (!ResourceContextKey._uriEquals(this._resourceKey.get(), value)) { - this._contextKeyService.bufferChangeEvents(() => { - this._resourceKey.set(value); -- this._schemeKey.set(value ? value.scheme : null); -+ // NOTE@coder: Fixes source control context menus (#1104). -+ this._schemeKey.set(value ? (value.scheme === Schemas.vscodeRemote ? Schemas.file : value.scheme) : null); - this._filenameKey.set(value ? basename(value) : null); - this._dirnameKey.set(value ? dirname(value).fsPath : null); - this._pathKey.set(value ? value.fsPath : null); -diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts -index c6d98b601a3b6966e8a99d2c05b3b7f02b08e6ca..c2a76fbd75d24d509f312cb8bf094eb297374f04 100644 ---- a/src/vs/workbench/contrib/remote/browser/remote.ts -+++ b/src/vs/workbench/contrib/remote/browser/remote.ts -@@ -778,17 +778,30 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { - } - switch (e.type) { - case PersistentConnectionEventType.ConnectionLost: -+ break; -+ case PersistentConnectionEventType.ReconnectionWait: -+ const BACKGROUND_RECONNECT_THRESHOLD = 2; -+ // If the first reconnect fails, we show the popup. -+ // This corresponds to about 5s wait. -+ if (e.connectionAttempt < BACKGROUND_RECONNECT_THRESHOLD) { -+ break; -+ } -+ - if (!visibleProgress) { - visibleProgress = showProgress(ProgressLocation.Dialog, [reconnectButton, reloadButton]); - } - visibleProgress.report(nls.localize('connectionLost', "Connection Lost")); -- break; -- case PersistentConnectionEventType.ReconnectionWait: -+ - reconnectWaitEvent = e; - visibleProgress = showProgress(lastLocation || ProgressLocation.Notification, [reconnectButton, reloadButton]); - visibleProgress.startTimer(Date.now() + 1000 * e.durationSeconds); - break; - case PersistentConnectionEventType.ReconnectionRunning: -+ if (!visibleProgress) { -+ // Our background reconnection threshold hasn't been hit yet. -+ break; -+ } -+ - visibleProgress = showProgress(lastLocation || ProgressLocation.Notification, [reloadButton]); - visibleProgress.report(nls.localize('reconnectionRunning', "Attempting to reconnect...")); - -diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css -index 74f6922e98b4bb6a7fb100f5aac015afe9fc171b..3243a97c2d378013d96ffbe87e9df6dd4a66776d 100644 ---- a/src/vs/workbench/contrib/scm/browser/media/scm.css -+++ b/src/vs/workbench/contrib/scm/browser/media/scm.css -@@ -149,9 +149,11 @@ - margin-right: 8px; - } - --.scm-view .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { -- flex-grow: 100; --} -+/* NOTE@coder: Causes the label to shrink to zero width in Firefox due to -+ * overflow:hidden. This isn't right anyway, as far as I can tell. */ -+/* .scm-view .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { */ -+/* flex-grow: 100; */ -+/* } */ - - .scm-view .monaco-list .monaco-list-row .resource-group > .actions, - .scm-view .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { -diff --git a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts -index 6af6a4b7f42654ec6cc60e0ba5efd376919f3e04..3a8176951628e0b2528aae8796ba684c3ab53d9a 100644 ---- a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts -+++ b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts -@@ -4,6 +4,7 @@ - *--------------------------------------------------------------------------------------------*/ - - import { escape } from 'vs/base/common/strings'; -+import product from 'vs/platform/product/common/product'; - import { localize } from 'vs/nls'; - - export default () => ` -@@ -11,7 +12,7 @@ export default () => ` -
-
-

${escape(localize('welcomePage.vscode', "Visual Studio Code"))}

--

${escape(localize({ key: 'welcomePage.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved"))}

-+

VS Code v${product.version}

-
-
-
-@@ -32,6 +33,19 @@ export default () => ` - -

${escape(localize('welcomePage.noRecentFolders', "No recent folders"))}

-
-+
-+

code-server ${escape(localize('welcomePage.help', "Help"))}

-+ -+
-
-

${escape(localize('welcomePage.help', "Help"))}

-
    -diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.css b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.css -index 738ce140c1af76ee0017c59cc883578e966f5348..80833b7023ed5795bb3de303b54ec08d9dab9b94 100644 ---- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.css -+++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.css -@@ -94,7 +94,7 @@ - } - - .monaco-workbench .part.editor > .content .welcomePage .splash .section { -- margin-bottom: 5em; -+ margin-bottom: 3em; - } - - .monaco-workbench .part.editor > .content .welcomePage .splash ul { -diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts -index 4a61a79fe447e2aa238af568791bff1e0cec4d29..69cc2e4331a3b04d05d79632920f5c5bbfa924e8 100644 ---- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts -+++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts -@@ -328,7 +328,7 @@ class WelcomePage extends Disposable { - - const prodName = container.querySelector('.welcomePage .title .caption') as HTMLElement; - if (prodName) { -- prodName.textContent = this.productService.nameLong; -+ prodName.textContent = `code-server v${this.productService.codeServerVersion}`; - } - - recentlyOpened.then(({ workspaces }) => { -diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts -index ed4f26407391bd62219a9f8245a5cd63a7cb7488..92f26d1b082f80475cf76409a4569e948e9e0bd9 100644 ---- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts -+++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts -@@ -130,6 +130,8 @@ export class SimpleNativeWorkbenchEnvironmentService implements INativeWorkbench - extensionsPath?: string | undefined; - extensionsDownloadPath: string = undefined!; - builtinExtensionsPath: string = undefined!; -+ extraExtensionPaths: string[] = undefined!; -+ extraBuiltinExtensionPaths: string[] = undefined!; - - driverHandle?: string | undefined; - -diff --git a/src/vs/workbench/services/dialogs/browser/dialogService.ts b/src/vs/workbench/services/dialogs/browser/dialogService.ts -index 85d83f37da179a1e39266cf72a02e971f590308e..0659738b36df1747c9afcabf8d9abf26c890990b 100644 ---- a/src/vs/workbench/services/dialogs/browser/dialogService.ts -+++ b/src/vs/workbench/services/dialogs/browser/dialogService.ts -@@ -125,11 +125,12 @@ export class DialogService implements IDialogService { - async about(): Promise { - const detailString = (useAgo: boolean): string => { - return nls.localize('aboutDetail', -- "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", -+ "code-server: v{4}\n VS Code: v{0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", - this.productService.version || 'Unknown', - this.productService.commit || 'Unknown', - this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown', -- navigator.userAgent -+ navigator.userAgent, -+ this.productService.codeServerVersion || 'Unknown', - ); - }; - -diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts -index a8d43045ecc8cbe04b3f8440cff16d42aadbcad0..d051473515e35b331672b780109bd40229153c8c 100644 ---- a/src/vs/workbench/services/environment/browser/environmentService.ts -+++ b/src/vs/workbench/services/environment/browser/environmentService.ts -@@ -119,8 +119,25 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment - @memoize - get logFile(): URI { return joinPath(this.options.logsPath, 'window.log'); } - -+ // NOTE@coder: Use the same path in // ../../../../platform/environment/node/environmentService.ts -+ // and don't use the user data scheme. This solves two problems: -+ // 1. Extensions running in the browser (like Vim) might use these paths -+ // directly instead of using the file service and most likely can't write -+ // to `/User` on disk. -+ // 2. Settings will be stored in the file system instead of in browser -+ // storage. Using browser storage makes sharing or seeding settings -+ // between browsers difficult. We may want to revisit this once/if we get -+ // settings sync. - @memoize -- get userRoamingDataHome(): URI { return URI.file('/User').with({ scheme: Schemas.userData }); } -+ get userRoamingDataHome(): URI { return joinPath(URI.file(this.userDataPath).with({ scheme: Schemas.vscodeRemote }), 'User'); } -+ @memoize -+ get userDataPath(): string { -+ const dataPath = this.payload?.get('userDataPath'); -+ if (!dataPath) { -+ throw new Error('userDataPath was not provided to environment service'); -+ } -+ return dataPath; -+ } - - @memoize - get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); } -@@ -301,7 +318,12 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment - extensionHostDebugEnvironment.params.port = parseInt(value); - break; - case 'enableProposedApi': -- extensionHostDebugEnvironment.extensionEnabledProposedApi = []; -+ try { -+ extensionHostDebugEnvironment.extensionEnabledProposedApi = JSON.parse(value); -+ } catch (error) { -+ console.error(error); -+ extensionHostDebugEnvironment.extensionEnabledProposedApi = []; -+ } - break; - } - } -diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts -index 50d4d812b76f09435fcff8148aac4ceeaeb30873..faacf88fcef119f9f959739656d64a84c8f64cbf 100644 ---- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts -+++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts -@@ -221,7 +221,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench - } - } - } -- return true; -+ return false; // NOTE@coder: Don't disable anything by extensionKind. - } - return false; - } -diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts -index de7e301d3f0c67ce662827f61427a5a7b3616b9f..877ea8e11e6e6d34b9a8fe16287af309e569285e 100644 ---- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts -+++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts -@@ -251,7 +251,9 @@ export class ExtensionManagementService extends Disposable implements IWorkbench - - // Install Language pack on all servers - if (isLanguagePackExtension(manifest)) { -- servers.push(...this.servers); -+ // NOTE@coder: It does not appear language packs can be installed on the web -+ // extension management server at this time. Filter out the web to fix this. -+ servers.push(...this.servers.filter(s => s !== this.extensionManagementServerService.webExtensionManagementServer)); - } else { - const server = this.getExtensionManagementServerToInstall(manifest); - if (server) { -@@ -320,6 +322,11 @@ export class ExtensionManagementService extends Disposable implements IWorkbench - return this.extensionManagementServerService.webExtensionManagementServer; - } - -+ // NOTE@coder: Fall back to installing on the remote server. -+ if (this.extensionManagementServerService.remoteExtensionManagementServer) { -+ return this.extensionManagementServerService.remoteExtensionManagementServer; -+ } -+ - return undefined; - } - -diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts -index 1dff19bf177eff24f722b748b79835a653241c4d..01ce9bc00cc39c27e75db006425c359f813a4719 100644 ---- a/src/vs/workbench/services/extensions/browser/extensionService.ts -+++ b/src/vs/workbench/services/extensions/browser/extensionService.ts -@@ -87,7 +87,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten - if (code === ExtensionHostExitCode.StartTimeout10s) { - this._notificationService.prompt( - Severity.Error, -- nls.localize('extensionService.startTimeout', "The Web Worker Extension Host did not start in 10s."), -+ nls.localize('extensionService.startTimeout', 'The Web Worker Extension Host did not start in 10s.'), - [] - ); - return; -@@ -177,8 +177,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten - this._remoteAgentService.getEnvironment(), - this._remoteAgentService.scanExtensions() - ]); -- localExtensions = this._checkEnabledAndProposedAPI(localExtensions); - remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions); -+ // NOTE@coder: Include remotely hosted extensions that should run locally. -+ localExtensions = this._checkEnabledAndProposedAPI(localExtensions) -+ .concat(remoteExtensions.filter(ext => !ext.browser && ext.extensionKind && (ext.extensionKind === 'web' || ext.extensionKind.includes('web')))); - - const remoteAgentConnection = this._remoteAgentService.getConnection(); - this._runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, remoteExtensions); -@@ -188,7 +190,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten - - const result = this._registry.deltaExtensions(remoteExtensions.concat(localExtensions), []); - if (result.removedDueToLooping.length > 0) { -- this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); -+ this._logOrShowMessage(Severity.Error, nls.localize('looping', 'The following extensions contain dependency loops and have been disabled: {0}', result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); - } - - if (remoteEnv && remoteAgentConnection) { -diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts -index 65e532ee58dfc06ed944846d01b885cb8f260ebc..0b6282fde7ad03c7ea9872a777cbf487253abed1 100644 ---- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts -+++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts -@@ -37,7 +37,8 @@ export function canExecuteOnWorkspace(manifest: IExtensionManifest, productServi - - export function canExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { - const extensionKind = getExtensionKind(manifest, productService, configurationService); -- return extensionKind.some(kind => kind === 'web'); -+ // NOTE@coder: Hardcode vim for now. -+ return extensionKind.some(kind => kind === 'web') || manifest.name === 'vim'; - } - - export function getExtensionKind(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): ExtensionKind[] { -diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts -index e39d131fe7b1dd4bd1093fedb8faba8e1fe969e8..5529222b24398100e544045d916b28db278f58a2 100644 ---- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts -+++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts -@@ -16,11 +16,12 @@ import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; - import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage, IExtHostReduceGraceTimeMessage, ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; - import { ExtensionHostMain, IExitFn } from 'vs/workbench/services/extensions/common/extensionHostMain'; - import { VSBuffer } from 'vs/base/common/buffer'; --import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc'; -+import { IURITransformer, URITransformer } from 'vs/base/common/uriIpc'; - import { exists } from 'vs/base/node/pfs'; - import { realpath } from 'vs/base/node/extpath'; - import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; - import { RunOnceScheduler } from 'vs/base/common/async'; -+import * as proxyAgent from 'vs/base/node/proxy_agent'; - - import 'vs/workbench/api/common/extHost.common.services'; - import 'vs/workbench/api/node/extHost.node.services'; -@@ -57,12 +58,13 @@ const args = minimist(process.argv.slice(2), { - const Module = require.__$__nodeRequire('module') as any; - const originalLoad = Module._load; - -- Module._load = function (request: string) { -+ Module._load = function (request: string, parent: object, isMain: boolean) { - if (request === 'natives') { - throw new Error('Either the extension or a NPM dependency is using the "natives" node module which is unsupported as it can cause a crash of the extension host. Click [here](https://go.microsoft.com/fwlink/?linkid=871887) to find out more'); - } - -- return originalLoad.apply(this, arguments); -+ // NOTE@coder: Map node_module.asar requests to regular node_modules. -+ return originalLoad.apply(this, [request.replace(/node_modules\.asar(\.unpacked)?/, 'node_modules'), parent, isMain]); - }; - })(); - -@@ -135,8 +137,11 @@ function _createExtHostProtocol(): Promise { - - // Wait for rich client to reconnect - protocol.onSocketClose(() => { -- // The socket has closed, let's give the renderer a certain amount of time to reconnect -- disconnectRunner1.schedule(); -+ // NOTE@coder: Inform the server so we can manage offline -+ // connections there instead. Our goal is to persist connections -+ // forever (to a reasonable point) to account for things like -+ // hibernating overnight. -+ process.send!({ type: 'VSCODE_EXTHOST_DISCONNECTED' }); - }); - } - } -@@ -295,6 +300,7 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise { -+ proxyAgent.monkeyPatch(true); - - const protocol = await createExtHostProtocol(); - const renderer = await connectToRenderer(protocol); -@@ -313,11 +319,9 @@ export async function startExtensionHostProcess(): Promise { - - // Attempt to load uri transformer - let uriTransformer: IURITransformer | null = null; -- if (initData.remote.authority && args.uriTransformerPath) { -+ if (initData.remote.authority) { - try { -- const rawURITransformerFactory = require.__$__nodeRequire(args.uriTransformerPath); -- const rawURITransformer = rawURITransformerFactory(initData.remote.authority); -- uriTransformer = new URITransformer(rawURITransformer); -+ uriTransformer = new URITransformer(initData.remote.authority); - } catch (e) { - console.error(e); - } -diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts -index b39a5cbb9eadbc046144d2e76d26a9b0e950ddaa..3b4cc7274e149ee10dba0dbbb09cf25939091f4b 100644 ---- a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts -+++ b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts -@@ -15,7 +15,11 @@ - require.config({ - baseUrl: monacoBaseUrl, - catchError: true, -- createTrustedScriptURL: (value: string) => value -+ createTrustedScriptURL: (value: string) => value, -+ paths: { -+ '@coder/node-browser': `../node_modules/@coder/node-browser/out/client/client.js`, -+ '@coder/requirefs': `../node_modules/@coder/requirefs/out/requirefs.js`, -+ } - }); - - require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err)); -diff --git a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts -index d7aefde89c74bc6096d6e66c45368c8582594efa..9758f3bb96b48603251336e6a64e270ee89744f0 100644 ---- a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts -+++ b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts -@@ -5,8 +5,8 @@ - - import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; - import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; --import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; - import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; - - // @ts-ignore: interface is implemented via proxy - export class LocalizationsService implements ILocalizationsService { -@@ -14,9 +14,9 @@ export class LocalizationsService implements ILocalizationsService { - declare readonly _serviceBrand: undefined; - - constructor( -- @ISharedProcessService sharedProcessService: ISharedProcessService, -+ @IRemoteAgentService remoteAgentService: IRemoteAgentService, - ) { -- return createChannelSender(sharedProcessService.getChannel('localizations')); -+ return createChannelSender(remoteAgentService.getConnection()!.getChannel('localizations')); - } - } - -diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts -index 509f8ac8ce3a689386e439302a53c27e4fdfcef7..2bf9a737bd0dbfa1e604acfc890be45823f02ebe 100644 ---- a/src/vs/workbench/workbench.web.main.ts -+++ b/src/vs/workbench/workbench.web.main.ts -@@ -35,7 +35,8 @@ import 'vs/workbench/services/textfile/browser/browserTextFileService'; - import 'vs/workbench/services/keybinding/browser/keymapService'; - import 'vs/workbench/services/extensions/browser/extensionService'; - import 'vs/workbench/services/extensionManagement/common/extensionManagementServerService'; --import 'vs/workbench/services/telemetry/browser/telemetryService'; -+// NOTE@coder: We send it all to the server side to be processed there instead. -+// import 'vs/workbench/services/telemetry/browser/telemetryService'; - import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; - import 'vs/workbench/services/credentials/browser/credentialsService'; - import 'vs/workbench/services/url/browser/urlService'; -diff --git a/yarn.lock b/yarn.lock -index ff358cb6a10984868ed5a5aed5729ac6eb8ebeb7..c73be6d8e9f9b213aeee2b4c22b53fc5d4184c56 100644 ---- a/yarn.lock -+++ b/yarn.lock -@@ -140,6 +140,23 @@ - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -+"@coder/logger@1.1.16": -+ version "1.1.16" -+ resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.1.16.tgz#ee5b1b188f680733f35c11b065bbd139d618c1e1" -+ integrity sha512-X6VB1++IkosYY6amRAiMvuvCf12NA4+ooX+gOuu5bJIkdjmh4Lz7QpJcWRdgxesvo1msriDDr9E/sDbIWf6vsQ== -+ -+"@coder/node-browser@^1.0.8": -+ version "1.0.8" -+ resolved "https://registry.yarnpkg.com/@coder/node-browser/-/node-browser-1.0.8.tgz#c22f581b089ad7d95ad1362fd351c57b7fbc6e70" -+ integrity sha512-NLF9sYMRCN9WK1C224pHax1Cay3qKypg25BhVg7VfNbo3Cpa3daata8RF/rT8JK3lPsu8PmFgDRQjzGC9X1Lrw== -+ -+"@coder/requirefs@^1.1.5": -+ version "1.1.5" -+ resolved "https://registry.yarnpkg.com/@coder/requirefs/-/requirefs-1.1.5.tgz#259db370d563a79a96fb150bc9d69c7db6edc9fb" -+ integrity sha512-3jB47OFCql9+9FI6Vc4YX0cfFnG5rxBfrZUH45S4XYtYGOz+/Xl4h4d2iMk50b7veHkeSWGlB4VHC3UZ16zuYQ== -+ optionalDependencies: -+ jszip "2.6.0" -+ - "@electron/get@^1.0.1": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.7.2.tgz#286436a9fb56ff1a1fcdf0e80131fd65f4d1e0fd" -@@ -172,6 +189,11 @@ - dependencies: - defer-to-connect "^1.0.1" - -+"@tootallnate/once@1": -+ version "1.1.2" -+ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" -+ integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -+ - "@types/applicationinsights@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@types/applicationinsights/-/applicationinsights-0.20.0.tgz#fa7b36dc954f635fa9037cad27c378446b1048fb" -@@ -634,6 +656,13 @@ agent-base@^4.3.0: - dependencies: - es6-promisify "^5.0.0" - -+agent-base@^6.0.0: -+ version "6.0.2" -+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" -+ integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== -+ dependencies: -+ debug "4" -+ - agent-base@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" -@@ -1045,6 +1074,13 @@ assign-symbols@^1.0.0: - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -+ast-types@^0.13.2: -+ version "0.13.4" -+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" -+ integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== -+ dependencies: -+ tslib "^2.0.1" -+ - astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" -@@ -1464,6 +1500,11 @@ builtin-status-codes@^3.0.0: - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= - -+bytes@3.1.0: -+ version "3.1.0" -+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" -+ integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -+ - cacache@^10.0.4: - version "10.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" -@@ -2365,6 +2406,11 @@ dashdash@^1.12.0: - dependencies: - assert-plus "^1.0.0" - -+data-uri-to-buffer@3: -+ version "3.0.1" -+ resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" -+ integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== -+ - date-fns@^2.0.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.14.0.tgz#359a87a265bb34ef2e38f93ecf63ac453f9bc7ba" -@@ -2513,6 +2559,15 @@ defined@^1.0.0: - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= - -+degenerator@^2.2.0: -+ version "2.2.0" -+ resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-2.2.0.tgz#49e98c11fa0293c5b26edfbb52f15729afcdb254" -+ integrity sha512-aiQcQowF01RxFI4ZLFMpzyotbQonhNpBao6dkI8JPk5a+hmSjR5ErHp2CQySmQe8os3VBqLCIh87nDBgZXvsmg== -+ dependencies: -+ ast-types "^0.13.2" -+ escodegen "^1.8.1" -+ esprima "^4.0.0" -+ - del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" -@@ -2546,6 +2601,11 @@ denodeify@^1.2.1: - resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" - integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= - -+depd@~1.1.2: -+ version "1.1.2" -+ resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" -+ integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -+ - des.js@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" -@@ -2931,6 +2991,18 @@ escape-string-regexp@^2.0.0: - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -+escodegen@^1.8.1: -+ version "1.14.3" -+ resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" -+ integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== -+ dependencies: -+ esprima "^4.0.1" -+ estraverse "^4.2.0" -+ esutils "^2.0.2" -+ optionator "^0.8.1" -+ optionalDependencies: -+ source-map "~0.6.1" -+ - eslint-plugin-jsdoc@^19.1.0: - version "19.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-19.1.0.tgz#fcc17f0378fdd6ee1c847a79b7211745cb05d014" -@@ -3126,6 +3198,11 @@ esprima@^4.0.0: - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" - integrity sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw== - -+esprima@^4.0.1: -+ version "4.0.1" -+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" -+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -+ - esquery@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" -@@ -3146,6 +3223,11 @@ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= - -+estraverse@^4.2.0: -+ version "4.3.0" -+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" -+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -+ - esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" -@@ -3435,6 +3517,11 @@ file-uri-to-path@1.0.0: - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -+file-uri-to-path@2: -+ version "2.0.0" -+ resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz#7b415aeba227d575851e0a5b0c640d7656403fba" -+ integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg== -+ - filename-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" -@@ -3746,6 +3833,14 @@ fstream@^1.0.2: - mkdirp ">=0.5 0" - rimraf "2" - -+ftp@^0.3.10: -+ version "0.3.10" -+ resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" -+ integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= -+ dependencies: -+ readable-stream "1.1.x" -+ xregexp "2.0.0" -+ - function-bind@^1.0.2, function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" -@@ -3799,6 +3894,18 @@ get-stream@^5.1.0: - dependencies: - pump "^3.0.0" - -+get-uri@3: -+ version "3.0.2" -+ resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-3.0.2.tgz#f0ef1356faabc70e1f9404fa3b66b2ba9bfc725c" -+ integrity sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg== -+ dependencies: -+ "@tootallnate/once" "1" -+ data-uri-to-buffer "3" -+ debug "4" -+ file-uri-to-path "2" -+ fs-extra "^8.1.0" -+ ftp "^0.3.10" -+ - get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" -@@ -4541,6 +4648,17 @@ http-cache-semantics@^4.0.0: - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz#495704773277eeef6e43f9ab2c2c7d259dda25c5" - integrity sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew== - -+http-errors@1.7.3: -+ version "1.7.3" -+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" -+ integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== -+ dependencies: -+ depd "~1.1.2" -+ inherits "2.0.4" -+ setprototypeof "1.1.1" -+ statuses ">= 1.5.0 < 2" -+ toidentifier "1.0.0" -+ - http-proxy-agent@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" -@@ -4549,6 +4667,15 @@ http-proxy-agent@^2.1.0: - agent-base "4" - debug "3.1.0" - -+http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: -+ version "4.0.1" -+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" -+ integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== -+ dependencies: -+ "@tootallnate/once" "1" -+ agent-base "6" -+ debug "4" -+ - http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" -@@ -4563,6 +4690,14 @@ https-browserify@^1.0.0: - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= - -+https-proxy-agent@5, https-proxy-agent@^5.0.0: -+ version "5.0.0" -+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" -+ integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== -+ dependencies: -+ agent-base "6" -+ debug "4" -+ - https-proxy-agent@^2.2.3: - version "2.2.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" -@@ -4579,14 +4714,6 @@ https-proxy-agent@^4.0.0: - agent-base "5" - debug "4" - --https-proxy-agent@^5.0.0: -- version "5.0.0" -- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" -- integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== -- dependencies: -- agent-base "6" -- debug "4" -- - husky@^0.13.1: - version "0.13.4" - resolved "https://registry.yarnpkg.com/husky/-/husky-0.13.4.tgz#48785c5028de3452a51c48c12c4f94b2124a1407" -@@ -4602,18 +4729,18 @@ iconv-lite-umd@0.6.8: - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" - integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== - --iconv-lite@^0.4.19: -- version "0.4.19" -- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" -- integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== -- --iconv-lite@^0.4.24: -+iconv-lite@0.4.24, iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -+iconv-lite@^0.4.19: -+ version "0.4.19" -+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" -+ integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== -+ - iconv-lite@^0.4.4: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" -@@ -4704,7 +4831,7 @@ inherits@2.0.1: - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - --inherits@^2.0.4: -+inherits@2.0.4, inherits@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -@@ -5403,6 +5530,13 @@ jsprim@^1.2.2: - json-schema "0.2.3" - verror "1.10.0" - -+jszip@2.6.0: -+ version "2.6.0" -+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-2.6.0.tgz#7fb3e9c2f11c8a9840612db5dabbc8cf3a7534b7" -+ integrity sha1-f7PpwvEciphAYS212rvIzzp1NLc= -+ dependencies: -+ pako "~1.0.0" -+ - just-debounce@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea" -@@ -5983,26 +6117,11 @@ minimatch@0.3: - dependencies: - brace-expansion "^1.1.7" - --minimist@0.0.8: -- version "0.0.8" -- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -- integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -- --minimist@^1.2.0: -- version "1.2.0" -- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -- integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -- --minimist@^1.2.5: -+minimist@0.0.8, minimist@^1.2.0, minimist@^1.2.5, minimist@~0.0.1: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - --minimist@~0.0.1: -- version "0.0.10" -- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" -- integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= -- - minipass@^2.2.1, minipass@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" -@@ -6232,6 +6351,11 @@ neo-async@^2.6.1: - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" - integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== - -+netmask@^1.0.6: -+ version "1.0.6" -+ resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" -+ integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= -+ - nice-try@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" -@@ -6581,6 +6705,18 @@ optimist@^0.6.1: - minimist "~0.0.1" - wordwrap "~0.0.2" - -+optionator@^0.8.1, optionator@^0.8.3: -+ version "0.8.3" -+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" -+ integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== -+ dependencies: -+ deep-is "~0.1.3" -+ fast-levenshtein "~2.0.6" -+ levn "~0.3.0" -+ prelude-ls "~1.1.2" -+ type-check "~0.3.2" -+ word-wrap "~1.2.3" -+ - optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" -@@ -6593,18 +6729,6 @@ optionator@^0.8.2: - type-check "~0.3.2" - wordwrap "~1.0.0" - --optionator@^0.8.3: -- version "0.8.3" -- resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" -- integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== -- dependencies: -- deep-is "~0.1.3" -- fast-levenshtein "~2.0.6" -- levn "~0.3.0" -- prelude-ls "~1.1.2" -- type-check "~0.3.2" -- word-wrap "~1.2.3" -- - ordered-read-streams@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz#7137e69b3298bb342247a1bbee3881c80e2fd78b" -@@ -6744,6 +6868,35 @@ p-try@^2.0.0: - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" - integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ== - -+pac-proxy-agent@^4.1.0: -+ version "4.1.0" -+ resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-4.1.0.tgz#66883eeabadc915fc5e95457324cb0f0ac78defb" -+ integrity sha512-ejNgYm2HTXSIYX9eFlkvqFp8hyJ374uDf0Zq5YUAifiSh1D6fo+iBivQZirGvVv8dCYUsLhmLBRhlAYvBKI5+Q== -+ dependencies: -+ "@tootallnate/once" "1" -+ agent-base "6" -+ debug "4" -+ get-uri "3" -+ http-proxy-agent "^4.0.1" -+ https-proxy-agent "5" -+ pac-resolver "^4.1.0" -+ raw-body "^2.2.0" -+ socks-proxy-agent "5" -+ -+pac-resolver@^4.1.0: -+ version "4.1.0" -+ resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-4.1.0.tgz#4b12e7d096b255a3b84e53f6831f32e9c7e5fe95" -+ integrity sha512-d6lf2IrZJJ7ooVHr7BfwSjRO1yKSJMaiiWYSHcrxSIUtZrCa4KKGwcztdkZ/E9LFleJfjoi1yl+XLR7AX24nbQ== -+ dependencies: -+ degenerator "^2.2.0" -+ ip "^1.1.5" -+ netmask "^1.0.6" -+ -+pako@~1.0.0: -+ version "1.0.11" -+ resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" -+ integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== -+ - pako@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" -@@ -7439,7 +7592,21 @@ proto-list@~1.2.1: - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - --proxy-from-env@^1.1.0: -+proxy-agent@^4.0.0: -+ version "4.0.0" -+ resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-4.0.0.tgz#a92976af3fbc7d846f2e850e2ac5ac6ca3fb74c7" -+ integrity sha512-8P0Y2SkwvKjiGU1IkEfYuTteioMIDFxPL4/j49zzt5Mz3pG1KO+mIrDG1qH0PQUHTTczjwGcYl+EzfXiFj5vUQ== -+ dependencies: -+ agent-base "^6.0.0" -+ debug "4" -+ http-proxy-agent "^4.0.0" -+ https-proxy-agent "^5.0.0" -+ lru-cache "^5.1.1" -+ pac-proxy-agent "^4.1.0" -+ proxy-from-env "^1.0.0" -+ socks-proxy-agent "^5.0.0" -+ -+proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -@@ -7607,6 +7774,16 @@ randomfill@^1.0.3: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -+raw-body@^2.2.0: -+ version "2.4.1" -+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" -+ integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== -+ dependencies: -+ bytes "3.1.0" -+ http-errors "1.7.3" -+ iconv-lite "0.4.24" -+ unpipe "1.0.0" -+ - rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" -@@ -7673,6 +7850,16 @@ read@^1.0.7: - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -+readable-stream@1.1.x: -+ version "1.1.14" -+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" -+ integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= -+ dependencies: -+ core-util-is "~1.0.0" -+ inherits "~2.0.1" -+ isarray "0.0.1" -+ string_decoder "~0.10.x" -+ - "readable-stream@2 || 3": - version "3.1.1" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.1.1.tgz#ed6bbc6c5ba58b090039ff18ce670515795aeb06" -@@ -8296,6 +8483,11 @@ setimmediate@^1.0.4: - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -+setprototypeof@1.1.1: -+ version "1.1.1" -+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" -+ integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== -+ - sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" -@@ -8374,6 +8566,11 @@ smart-buffer@4.0.2: - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.2.tgz#5207858c3815cc69110703c6b94e46c15634395d" - integrity sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw== - -+smart-buffer@^4.1.0: -+ version "4.1.0" -+ resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" -+ integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== -+ - snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" -@@ -8411,6 +8608,15 @@ sntp@2.x.x: - dependencies: - hoek "4.x.x" - -+socks-proxy-agent@5, socks-proxy-agent@^5.0.0: -+ version "5.0.0" -+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" -+ integrity sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA== -+ dependencies: -+ agent-base "6" -+ debug "4" -+ socks "^2.3.3" -+ - socks-proxy-agent@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" -@@ -8419,6 +8625,14 @@ socks-proxy-agent@^4.0.1: - agent-base "~4.2.1" - socks "~2.3.2" - -+socks@^2.3.3: -+ version "2.5.1" -+ resolved "https://registry.yarnpkg.com/socks/-/socks-2.5.1.tgz#7720640b6b5ec9a07d556419203baa3f0596df5f" -+ integrity sha512-oZCsJJxapULAYJaEYBSzMcz8m3jqgGrHaGhkmU/o/PQfFWYWxkAaA0UMGImb6s6tEXfKi959X6VJjMMQ3P6TTQ== -+ dependencies: -+ ip "^1.1.5" -+ smart-buffer "^4.1.0" -+ - socks@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.2.tgz#ade388e9e6d87fdb11649c15746c578922a5883e" -@@ -8612,6 +8826,11 @@ static-extend@^0.1.1: - define-property "^0.2.5" - object-copy "^0.1.0" - -+"statuses@>= 1.5.0 < 2": -+ version "1.5.0" -+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" -+ integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -+ - stream-browserify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" -@@ -9170,6 +9389,11 @@ to-through@^2.0.0: - dependencies: - through2 "^2.0.3" - -+toidentifier@1.0.0: -+ version "1.0.0" -+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" -+ integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -+ - tough-cookie@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" -@@ -9219,6 +9443,11 @@ tslib@^1.8.1, tslib@^1.9.0: - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" - integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== - -+tslib@^2.0.1: -+ version "2.0.3" -+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" -+ integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== -+ - tsutils@^3.17.1: - version "3.17.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" -@@ -9397,6 +9626,11 @@ universalify@^0.1.0: - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -+unpipe@1.0.0: -+ version "1.0.0" -+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" -+ integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= -+ - unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" -@@ -10049,6 +10283,11 @@ xmldom@0.1.x: - resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" - integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= - -+xregexp@2.0.0: -+ version "2.0.0" -+ resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" -+ integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= -+ - "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" diff --git a/package.json b/package.json index c3681d1d1..8407bd543 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,6 @@ "scripts": { "clean": "./ci/build/clean.sh", "vscode": "./ci/dev/vscode.sh", - "vscode:patch": "./ci/dev/patch-vscode.sh", - "vscode:diff": "./ci/dev/diff-vscode.sh", "vscode:reset": "./ci/dev/reset-vscode.sh", "build": "./ci/build/build-code-server.sh", "build:vscode": "./ci/build/build-vscode.sh",