"use strict";
var ExtensionNodeServiceImpl_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtensionNodeServiceImpl = void 0;
const tslib_1 = require("tslib");
const net_1 = tslib_1.__importDefault(require("net"));
const path_1 = tslib_1.__importDefault(require("path"));
const util_1 = tslib_1.__importDefault(require("util"));
const di_1 = require("@opensumi/di");
const connection_1 = require("@opensumi/ide-connection/lib/common/connection");
const server_handler_1 = require("@opensumi/ide-connection/lib/common/server-handler");
const ide_core_common_1 = require("@opensumi/ide-core-common");
const port_1 = require("@opensumi/ide-core-common/lib/node/port");
const ipc_1 = require("@opensumi/ide-core-common/lib/utils/ipc");
const ide_core_node_1 = require("@opensumi/ide-core-node");
const ide_utils_1 = require("@opensumi/ide-utils");
const common_1 = require("../common");
const extension_scanner_1 = require("./extension.scanner");
let ExtensionNodeServiceImpl = ExtensionNodeServiceImpl_1 = class ExtensionNodeServiceImpl {
    constructor() {
        this.LOG_TAG = 'ExtensionNodeServiceImpl:' + Date.now();
        this.extHostLogger = (0, ide_core_common_1.getDebugLogger)(ide_core_common_1.SupportLogNamespace.ExtensionHost);
        this.clientExtProcessMap = new Map();
        this.clientExtProcessInspectPortMap = new Map();
        this.clientExtProcessInitDeferredMap = new Map();
        this.clientExtProcessExtConnection = new Map();
        this.clientExtProcessExtConnectionDeferredMap = new Map();
        this.clientExtProcessExtConnectionServer = new Map();
        this.clientExtProcessFinishDeferredMap = new Map();
        this.clientExtProcessThresholdExitTimerMap = new Map();
        this.clientServiceMap = new Map();
        this.maybeZombieClients = new Set();
        // 最新的插件进程的对应的进程 Id
        this.latestExtProcessId = -1;
        this.inspectPort = 9889;
        this.onDidSetInspectPort = new ide_core_common_1.Emitter();
        this.extServerListenOptions = new Map();
        this.electronMainThreadListenPaths = new Map();
    }
    setConnectionServiceClient(clientId, serviceClient) {
        this.clientServiceMap.set(clientId, serviceClient);
    }
    async initialize() {
        await this.extensionHostManager.init();
        this.setExtProcessConnectionForward();
    }
    async getAllExtensions(scan, extensionCandidate, localization, extraMetaData = {}) {
        // 扫描内置插件和插件市场的插件目录
        this.extensionScanner = new extension_scanner_1.ExtensionScanner([...scan, this.appConfig.marketplace.extensionDir], localization, extensionCandidate, extraMetaData);
        return this.extensionScanner.run();
    }
    async getExtension(extensionPath, localization, extraMetaData) {
        return await extension_scanner_1.ExtensionScanner.getExtension(extensionPath, localization, extraMetaData);
    }
    async getIPCHandlerPath(name) {
        return await (0, ipc_1.normalizedIpcHandlerPathAsync)(name, true, this.appConfig.extHostIPCSockPath);
    }
    async getExtServerListenOption(clientId, extensionConnectOption) {
        if (!this.extServerListenOptions.has(clientId)) {
            const { mode = ide_core_common_1.ExtensionConnectModeOption.IPC, host } = extensionConnectOption || {};
            const options = {};
            if (mode === ide_core_common_1.ExtensionConnectModeOption.IPC) {
                options.path = await this.getIPCHandlerPath('ext_process');
            }
            else {
                options.port = await (0, port_1.findFreePort)(this.inspectPort, 10, 5000);
                options.host = host;
            }
            this.extServerListenOptions.set(clientId, options);
        }
        return this.extServerListenOptions.get(clientId);
    }
    async getElectronMainThreadListenPath(clientId) {
        if (!this.electronMainThreadListenPaths.has(clientId)) {
            this.electronMainThreadListenPaths.set(clientId, await this.getIPCHandlerPath('main_thread'));
        }
        return this.electronMainThreadListenPaths.get(clientId);
    }
    setExtProcessConnectionForward() {
        this.logger.log('setExtProcessConnectionForward', this.LOG_TAG);
        this._setMainThreadConnection(async ({ channel, clientId }) => {
            var _a;
            this.maybeZombieClients.delete(clientId);
            if (this.clientExtProcessExtConnectionDeferredMap.get(clientId)) {
                // means that we are creating the ext process or the ext process is created.
                await ((_a = this.clientExtProcessExtConnectionDeferredMap.get(clientId)) === null || _a === void 0 ? void 0 : _a.promise);
            }
            const extProcessId = this.clientExtProcessMap.get(clientId);
            const extProcessNotExist = (0, ide_core_common_1.isUndefined)(extProcessId) ||
                !((await this.extensionHostManager.isRunning(extProcessId)) && this.clientExtProcessExtConnection.has(clientId));
            if (extProcessNotExist) {
                this.logger.error(`${clientId} clientId process connection not exists, try to notify client to restart`);
                /**
                 * 如果前端与后端连接后发现没有对应的插件进程实例，那么通知前端重启插件进程
                 * 已知如下场景会出现这种情况：
                 * 1. 用户关闭电脑超过 ProcessCloseExitThreshold 设定的最大时间，插件进程被杀死后，前端再次建立连接时
                 * 2. Node 进程被杀死，插件进程也会被杀死
                 */
                this.restartExtProcessByClient(clientId);
                this.reporterService.point(ide_core_node_1.REPORT_NAME.EXTENSION_NOT_EXIST, clientId);
                return;
            }
            const extConnection = this.clientExtProcessExtConnection.get(clientId);
            const disposable1 = extConnection.onMessage((data) => {
                channel.sendBinary(data);
            });
            const disposable2 = channel.onBinary((data) => {
                extConnection.send(data);
            });
            extConnection.onceClose(() => {
                disposable1.dispose();
                disposable2.dispose();
            });
            // 连接恢复后清除销毁的定时器
            if (this.clientExtProcessThresholdExitTimerMap.has(clientId)) {
                const timer = this.clientExtProcessThresholdExitTimerMap.get(clientId);
                clearTimeout(timer);
            }
            this.logger.log(`setExtProcessConnectionForward clientId ${clientId}`);
        });
    }
    async createProcess(clientId, options) {
        this.logger.log(this.LOG_TAG, 'create extension process for client:', clientId);
        this.logger.log('appconfig exthost', this.appConfig.extHost);
        // 检查是否超过限制最大的进程数
        const processClientIdArr = Array.from(this.clientExtProcessMap.keys());
        const maxExtProcessCount = this.appConfig.maxExtProcessCount || ExtensionNodeServiceImpl_1.MaxExtProcessCount;
        if (processClientIdArr.length >= maxExtProcessCount) {
            const killProcessClientId = processClientIdArr[0];
            this.disposeClientExtProcess(killProcessClientId);
            this.logger.error(`Process count is over limit, max count is ${maxExtProcessCount}, try kill`, killProcessClientId);
        }
        await this._createExtServer(clientId, options);
        await this._createExtHostProcess(clientId, options);
    }
    async _createExtServer(clientId, options) {
        // 创建插件进程监听的 socket
        const extServerListenOptions = await this.getExtServerListenOption(clientId, options === null || options === void 0 ? void 0 : options.extensionConnectOption);
        // 先使用单个 server，再尝试单个 server 与多个进程进行连接
        const extServer = net_1.default.createServer();
        this.clientExtProcessExtConnectionServer.set(clientId, extServer);
        extServer.on('connection', (socket) => {
            var _a;
            this.logger.log('_setupExtHostConnection ext host connected');
            this.clientExtProcessExtConnection.set(clientId, new connection_1.NetSocketConnection(socket));
            (_a = this.clientExtProcessExtConnectionDeferredMap.get(clientId)) === null || _a === void 0 ? void 0 : _a.resolve();
        });
        this.clientExtProcessExtConnectionDeferredMap.set(clientId, new ide_core_node_1.Deferred());
        extServer.listen(extServerListenOptions, () => {
            this.logger.log(`${clientId} ext server listen on ${JSON.stringify(extServerListenOptions)}`);
        });
        // 重启时，旧的 path 已经不再使用，但是系统未清理，导致 listen 会失败，所以在连接关闭时，主动清理
        extServer.once('close', () => {
            this.extServerListenOptions.delete(clientId);
        });
    }
    async _createExtHostProcess(clientId, options) {
        var _a, _b;
        // 在新进程创建前销毁可能存在的僵尸进程
        this.destoryZombineClients();
        let preloadPath;
        let forkOptions = {
            // 防止 childProcess.stdout 为 null
            silent: true,
            env: {
                // 显式设置 env，因为需要和插件运行环境的 env merge
                ...process.env,
                ...(_a = options === null || options === void 0 ? void 0 : options.extHostSpawnOptions) === null || _a === void 0 ? void 0 : _a.env,
            },
        };
        // 软链模式下的路径兼容性存在问题
        if ((0, ide_core_node_1.isElectronNode)()) {
            this.logger.verbose('try get shell path for extension process');
            let shellPath;
            try {
                shellPath = (await (0, ide_core_node_1.getShellPath)()) || '';
                // 在某些机型上，可能存在由于权限问题导致的获取的 shell path 比当前给的 path 还少的情况，这种情况下对 PATH 做一下 merge
                if (shellPath && process.env.PATH) {
                    const paths = shellPath.split(':');
                    process.env.PATH.split(':').forEach((path) => {
                        if (paths.indexOf(path) === -1) {
                            paths.push(path);
                        }
                    });
                    shellPath = paths.join(':');
                }
                this.logger.verbose('shell path result: ' + shellPath);
            }
            catch (e) {
                this.logger.error('shell path error: ', e);
            }
            forkOptions = {
                ...forkOptions,
                env: {
                    ...forkOptions.env,
                    // 可能会有获取失败的情况
                    PATH: shellPath ? shellPath : process.env.PATH,
                },
            };
        }
        const forkArgs = [];
        const extServerListenOption = await this.getExtServerListenOption(clientId, options === null || options === void 0 ? void 0 : options.extensionConnectOption);
        let extProcessPath = '';
        forkOptions.execArgv = [];
        forkArgs.push(`--${common_1.KT_PROCESS_SOCK_OPTION_KEY}=${JSON.stringify(extServerListenOption)}`);
        if ((0, ide_core_node_1.isElectronNode)()) {
            extProcessPath = this.appConfig.extHost || process.env.EXTENSION_HOST_ENTRY;
        }
        else {
            preloadPath =
                process.env.EXT_MODE === 'js'
                    ? path_1.default.join(__dirname, '../../lib/hosted/ext.host.js')
                    : path_1.default.join(__dirname, '../hosted/ext.host.' + ide_utils_1.process.extFileType);
            if (process.env.EXT_MODE !== 'js' && ide_utils_1.process.extFileType === 'ts') {
                forkOptions.execArgv = forkOptions.execArgv.concat(['-r', 'ts-node/register', '-r', 'tsconfig-paths/register']);
            }
            forkArgs.push(`--kt-process-preload=${preloadPath}`);
            if (this.appConfig.extHost) {
                extProcessPath = this.appConfig.extHost;
            }
            else {
                extProcessPath =
                    process.env.EXT_MODE === 'js'
                        ? path_1.default.join(__dirname, '../../hosted/ext.process.js')
                        : path_1.default.join(__dirname, '../hosted/ext.process.' + ide_utils_1.process.extFileType);
            }
        }
        this.logger.log(`Extension host process path ${extProcessPath}`);
        // 注意只能传递可以序列化的数据
        forkArgs.push(`--${common_1.KT_APP_CONFIG_KEY}=${JSON.stringify({
            logDir: this.appConfig.logDir,
            logLevel: this.appConfig.logLevel,
            extLogServiceClassPath: this.appConfig.extLogServiceClassPath,
        })}`);
        if ((options === null || options === void 0 ? void 0 : options.enableDebugExtensionHost) || (0, ide_core_node_1.isDevelopment)()) {
            // 开发模式下指定调试端口时，尝试从指定的端口开始寻找可用的空闲端口
            // 避免打开多个窗口(多个插件进程)时端口被占用
            const port = await this.extensionHostManager.findDebugPort(this.inspectPort, 10, 5000);
            forkOptions.execArgv.push('--nolazy');
            if (options === null || options === void 0 ? void 0 : options.inspectExtensionHost) {
                forkOptions.execArgv.push(`--inspect=${options.inspectExtensionHost}:${port}`);
            }
            else {
                forkOptions.execArgv.push(`--inspect=${port}`);
            }
            this.clientExtProcessInspectPortMap.set(clientId, port);
        }
        if ((_b = options === null || options === void 0 ? void 0 : options.extHostSpawnOptions) === null || _b === void 0 ? void 0 : _b.execArgv) {
            forkOptions.execArgv = forkOptions.execArgv.concat(options.extHostSpawnOptions.execArgv);
        }
        const forkTimer = this.reporterService.time(`${clientId} fork ext process`);
        const extProcessId = await this.extensionHostManager.fork(extProcessPath, forkArgs, {
            ...forkOptions,
            ...this.appConfig.extHostForkOptions,
        });
        this.latestExtProcessId = extProcessId;
        this.logger.log(`Fork extension host process with id ${extProcessId}`);
        // 监听进程输出，用于获取调试端口
        this.extensionHostManager.onOutput(extProcessId, (output) => {
            const inspectorUrlMatch = output.data && output.data.match(/ws:\/\/([^\s]+:(\d+)\/[^\s]+)/);
            if (inspectorUrlMatch) {
                const port = Number(inspectorUrlMatch[2]);
                this.clientExtProcessInspectPortMap.set(clientId, port);
                this.onDidSetInspectPort.fire();
            }
            else {
                // 输出插件进程日志
                if (output.type === common_1.OutputType.STDERR) {
                    this.extHostLogger.error(util_1.default.format(output.data, ...output.format));
                }
                else {
                    this.extHostLogger.log(util_1.default.format(output.data, ...output.format));
                }
            }
        });
        this.extensionHostManager.onExit(extProcessId, async (code, signal) => {
            this.logger.log(`Extension host process ${extProcessId} exit by code ${code} signal ${signal}`);
            if (this.clientExtProcessMap.get(clientId) === extProcessId) {
                await this.disposeClientExtProcess(clientId, false, false);
                this.infoProcessCrash(clientId);
                this.reporterService.point(ide_core_node_1.REPORT_NAME.EXTENSION_CRASH, clientId, {
                    code,
                    signal,
                });
            }
            else {
                this.logger.log(`Extension host process ${extProcessId} exit by dispose`);
            }
        });
        this.clientExtProcessMap.set(clientId, extProcessId);
        const extProcessInitDeferred = new ide_core_node_1.Deferred();
        this.clientExtProcessInitDeferredMap.set(clientId, extProcessInitDeferred);
        this.processHandshake(extProcessId, forkTimer, clientId);
    }
    async ensureProcessReady(clientId) {
        var _a;
        if (!this.clientExtProcessInitDeferredMap.has(clientId)) {
            return false;
        }
        await ((_a = this.clientExtProcessInitDeferredMap.get(clientId)) === null || _a === void 0 ? void 0 : _a.promise);
        return true;
    }
    processHandshake(extProcessId, forkTimer, clientId) {
        const initHandler = (msg) => {
            var _a;
            if (msg === 'ready') {
                const duration = forkTimer.timeEnd();
                this.logger.log(`Starting extension host with pid ${extProcessId} (fork() took ${duration} ms).`);
                (_a = this.clientExtProcessInitDeferredMap.get(clientId)) === null || _a === void 0 ? void 0 : _a.resolve();
                this.clientExtProcessFinishDeferredMap.set(clientId, new ide_core_node_1.Deferred());
            }
            else if (msg === 'finish') {
                const finishDeferred = this.clientExtProcessFinishDeferredMap.get(clientId);
                if (finishDeferred) {
                    finishDeferred.resolve();
                }
            }
            else if (typeof msg === 'object' && msg.type === common_1.ProcessMessageType.REPORTER) {
                const reporterMessage = msg.data;
                if (reporterMessage.reportType === ide_core_node_1.REPORT_TYPE.PERFORMANCE) {
                    this.reporter.performance(reporterMessage.name, reporterMessage.data);
                }
                else if (reporterMessage.reportType === ide_core_node_1.REPORT_TYPE.POINT) {
                    this.reporter.point(reporterMessage.name, reporterMessage.data);
                }
            }
        };
        this.extensionHostManager.onMessage(extProcessId, initHandler);
    }
    async tryEnableInspectPort(clientId, delay) {
        if (this.clientExtProcessInspectPortMap.has(clientId)) {
            return true;
        }
        const extHostProcessId = this.clientExtProcessMap.get(clientId);
        if ((0, ide_core_common_1.isUndefined)(extHostProcessId)) {
            return false;
        }
        if (typeof process._debugProcess === 'function') {
            // use (undocumented) _debugProcess feature of node
            try {
                process._debugProcess(extHostProcessId);
            }
            catch (err) {
                this.logger.error(`Enable inspect port error \n ${err.message}`);
                return false;
            }
            await Promise.race([ide_core_common_1.Event.toPromise(this.onDidSetInspectPort.event), (0, ide_core_common_1.timeout)(delay || 1000)]);
            return typeof this.clientExtProcessInspectPortMap.get(clientId) === 'number';
        }
        else if (!ide_core_node_1.isWindows) {
            // use KILL USR1 on non-windows platforms (fallback)
            await this.extensionHostManager.kill(extHostProcessId, 'SIGUSR1');
            await Promise.race([ide_core_common_1.Event.toPromise(this.onDidSetInspectPort.event), (0, ide_core_common_1.timeout)(delay || 1000)]);
            return typeof this.clientExtProcessInspectPortMap.get(clientId) === 'number';
        }
        return false;
    }
    async getProcessInspectPort(clientId) {
        const extHostProcessId = this.clientExtProcessMap.get(clientId);
        if (!extHostProcessId || !(await this.extensionHostManager.isRunning(extHostProcessId))) {
            return;
        }
        return this.clientExtProcessInspectPortMap.get(clientId);
    }
    async _setMainThreadConnection(handler) {
        this.commonChannelPathHandler.register(common_1.CONNECTION_HANDLE_BETWEEN_EXTENSION_AND_MAIN_THREAD, {
            handler: (channel, clientId) => {
                handler({
                    channel,
                    clientId,
                });
                channel.onceClose(() => {
                    channel.dispose();
                    this.logger.log(`The connection client ${clientId} closed`);
                    this.maybeZombieClients.add(clientId);
                    this.closeExtProcessWhenConnectionClose(clientId);
                });
            },
            dispose: () => { },
        });
    }
    closeExtProcessWhenConnectionClose(connectionClientId) {
        var _a;
        if (this.clientExtProcessMap.has(connectionClientId)) {
            if ((0, ide_core_node_1.isElectronNode)()) {
                // in electron, if current connection is closed, kill the ext process immediately
                this.disposeClientExtProcess(connectionClientId);
            }
            else {
                // 为了保证体验，最后一个创建的插件进程断连后不杀死
                // https://github.com/opensumi/core/issues/3653
                if (this.clientExtProcessMap.get(connectionClientId) === this.latestExtProcessId) {
                    return;
                }
                /**
                 * in web, if current connection is closed, kill the ext process after a threshold
                 */
                const timer = setTimeout(() => {
                    this.logger.log(`Dispose client by connectionClientId ${connectionClientId}`);
                    this.disposeClientExtProcess(connectionClientId);
                }, (_a = this.appConfig.processCloseExitThreshold) !== null && _a !== void 0 ? _a : ExtensionNodeServiceImpl_1.ProcessCloseExitThreshold);
                this.clientExtProcessThresholdExitTimerMap.set(connectionClientId, timer);
            }
        }
    }
    infoProcessNotExist(clientId) {
        if (this.clientServiceMap.has(clientId)) {
            this.clientServiceMap.get(clientId).infoProcessNotExist();
            this.clientServiceMap.delete(clientId);
        }
    }
    /**
     * 如果插件进程已被销毁，如 websocket 连接断开超过 `ExtensionNodeServiceImpl.ProcessCloseExitThreshold` 时
     * 那么当用户重新连接至服务时，需要通知重启整个插件进程
     */
    restartExtProcessByClient(clientId) {
        if (this.clientServiceMap.has(clientId)) {
            this.clientServiceMap.get(clientId).restartExtProcessByClient();
        }
    }
    infoProcessCrash(clientId) {
        if (this.clientServiceMap.has(clientId)) {
            this.clientServiceMap.get(clientId).infoProcessCrash();
        }
    }
    async disposeClientExtProcess(clientId, info = true, killProcess = true) {
        const extProcessId = this.clientExtProcessMap.get(clientId);
        try {
            if (!(0, ide_core_common_1.isUndefined)(extProcessId)) {
                if (await this.extensionHostManager.isRunning(extProcessId)) {
                    await this.extensionHostManager.send(extProcessId, 'close');
                    // deactivate
                    // subscription
                    if (this.clientExtProcessFinishDeferredMap.has(clientId)) {
                        await this.clientExtProcessFinishDeferredMap.get(clientId).promise;
                    }
                }
                // extServer 关闭
                if (this.clientExtProcessExtConnectionServer.has(clientId)) {
                    this.clientExtProcessExtConnectionServer.get(clientId).close();
                    this.clientExtProcessExtConnectionServer.delete(clientId);
                }
                // connect 关闭
                if (this.clientExtProcessExtConnection.has(clientId)) {
                    this.clientExtProcessExtConnection.get(clientId).dispose();
                    this.clientExtProcessExtConnection.get(clientId).destroy();
                    this.clientExtProcessExtConnection.delete(clientId);
                }
                this.clientExtProcessExtConnectionDeferredMap.delete(clientId);
                this.clientExtProcessFinishDeferredMap.delete(clientId);
                this.clientExtProcessInitDeferredMap.delete(clientId);
                this.clientExtProcessMap.delete(clientId);
                this.maybeZombieClients.delete(clientId);
                if (killProcess) {
                    await this.extensionHostManager.treeKill(extProcessId);
                    await this.extensionHostManager.disposeProcess(extProcessId);
                }
                if (info) {
                    this.infoProcessNotExist(clientId);
                }
                this.logger.log(`Extension host process disposed by clientId ${clientId}`);
            }
        }
        catch (e) {
            this.logger.error(`Close extension host process when connection throw error\n${e.message}`);
        }
    }
    async getExtProcessId(clientId) {
        if (this.clientExtProcessMap.has(clientId)) {
            return this.clientExtProcessMap.get(clientId);
        }
        return null;
    }
    async disposeAllClientExtProcess() {
        await this.extensionHostManager.dispose();
    }
    destoryZombineClients() {
        for (const clientId of this.maybeZombieClients) {
            this.disposeClientExtProcess(clientId);
        }
    }
};
exports.ExtensionNodeServiceImpl = ExtensionNodeServiceImpl;
ExtensionNodeServiceImpl.MaxExtProcessCount = 5;
// ws 断开 5 分钟后杀掉插件进程
ExtensionNodeServiceImpl.ProcessCloseExitThreshold = 5 * 60 * 1000;
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_node_1.INodeLogger),
    tslib_1.__metadata("design:type", Object)
], ExtensionNodeServiceImpl.prototype, "logger", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_node_1.AppConfig),
    tslib_1.__metadata("design:type", Object)
], ExtensionNodeServiceImpl.prototype, "appConfig", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_node_1.IReporterService),
    tslib_1.__metadata("design:type", Object)
], ExtensionNodeServiceImpl.prototype, "reporterService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_node_1.IReporter),
    tslib_1.__metadata("design:type", Object)
], ExtensionNodeServiceImpl.prototype, "reporter", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(common_1.IExtensionHostManager),
    tslib_1.__metadata("design:type", Object)
], ExtensionNodeServiceImpl.prototype, "extensionHostManager", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(server_handler_1.CommonChannelPathHandler),
    tslib_1.__metadata("design:type", server_handler_1.CommonChannelPathHandler)
], ExtensionNodeServiceImpl.prototype, "commonChannelPathHandler", void 0);
tslib_1.__decorate([
    (0, ide_core_common_1.pMemoize)((clientId) => clientId),
    tslib_1.__metadata("design:type", Function),
    tslib_1.__metadata("design:paramtypes", [String, Object]),
    tslib_1.__metadata("design:returntype", Promise)
], ExtensionNodeServiceImpl.prototype, "createProcess", null);
exports.ExtensionNodeServiceImpl = ExtensionNodeServiceImpl = ExtensionNodeServiceImpl_1 = tslib_1.__decorate([
    (0, di_1.Injectable)()
], ExtensionNodeServiceImpl);
//# sourceMappingURL=extension.service.js.map