{"version":3,"sources":["../../../packages/core/data/powershell.ts"],"names":[],"mappings":"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","file":"powershell.d.ts","sourcesContent":["import { EMPTY, Observable, Observer, throwError } from 'rxjs';\r\nimport { AjaxError } from 'rxjs/ajax';\r\nimport { catchError, expand, mergeMap, tap } from 'rxjs/operators';\r\nimport { LogLevel } from '../diagnostics/log-level';\r\nimport { Logging } from '../diagnostics/logging';\r\nimport { SmeWebTelemetry } from '../diagnostics/sme-web-telemetry';\r\nimport { TelemetryEventStates } from '../diagnostics/sme-web-telemetry-models';\r\nimport { Strings } from '../generated/strings';\r\nimport { Disposable, DisposableLifetimeManager, Disposer } from './disposable';\r\nimport { headerConstants, HttpStatusCode } from './http-constants';\r\nimport { Net } from './net';\r\nimport { NodeConnection, NodeRequest, NodeRequestOptions } from './node-connection';\r\n\r\n/**\r\n * PowerShell run options.\r\n */\r\nexport interface PowerShellOptions extends NodeRequestOptions {\r\n    /**\r\n     * Close the runspace after the call.\r\n     * (default is false)\r\n     */\r\n    close?: boolean;\r\n\r\n    /**\r\n     * Close the runspace on error.\r\n     */\r\n    closeOnError?: boolean;\r\n\r\n    /**\r\n     * Timeout milliseconds to shutdown the session.\r\n     * (default is null and forever)\r\n     */\r\n    timeoutMs?: number;\r\n\r\n    /**\r\n     * Processing data progressively instead of waiting all result.\r\n     */\r\n    partial?: boolean;\r\n\r\n    /**\r\n     * Specify the initial waiting time before starting polling of result of invoke script.\r\n     */\r\n    waitTimeMs?: number;\r\n}\r\n\r\n/**\r\n * The node request options to control the underlying node connections in a powershell session\r\n */\r\nexport interface PowerShellSessionRequestOptions extends NodeRequestOptions {\r\n    /**\r\n     * Specify the session pool mode on the gateway.\r\n     */\r\n    automatic?: boolean;\r\n}\r\n\r\n/**\r\n * PowerShell context object interface.\r\n */\r\nexport interface PowerShellContext {\r\n    /**\r\n     * The node name.\r\n     */\r\n    nodeName: string;\r\n\r\n    /**\r\n     * The shared key name for runspace.\r\n     */\r\n    key: string;\r\n\r\n    /**\r\n     * The array of referenced containers.\r\n     */\r\n    lifetimes: DisposableLifetimeManager[];\r\n\r\n    /**\r\n     * The request options for the powershell session\r\n     */\r\n    requestOptions: PowerShellSessionRequestOptions;\r\n}\r\n\r\n/**\r\n * PowerShell command object.\r\n */\r\n export interface PowerShellCommandWithoutState {\r\n    /**\r\n     * The name of PowerShell module. (optional, it uses default module if not specified.)\r\n     */\r\n    module?: string;\r\n\r\n    /**\r\n     * The command name.\r\n     */\r\n    command: string;\r\n\r\n    /**\r\n     * The parameters (arguments).\r\n     */\r\n    parameters?: any;\r\n\r\n    /**\r\n     * Set if the command should be run in a local runspace.\r\n     */\r\n    useInProcRunspace?: boolean;\r\n\r\n    /**\r\n     * The script string.\r\n     */\r\n    script: string;\r\n}\r\n\r\n/**\r\n * PowerShell command object.\r\n */\r\nexport interface PowerShellCommand extends PowerShellCommandWithoutState  {\r\n    /**\r\n     * The state of command object.\r\n     */\r\n    state: string;\r\n}\r\n\r\n/**\r\n * PowerShell command queue item.\r\n */\r\nexport interface PowerShellCommandItem {\r\n    /**\r\n     * The command to execute.\r\n     */\r\n    command: PowerShellCommand;\r\n\r\n    /**\r\n     * Options for how to handle the command (reserved)\r\n     */\r\n    options?: PowerShellOptions;\r\n\r\n    /**\r\n     * Deferred object currently waiting for command run.\r\n     */\r\n    observer: Observer<any>;\r\n}\r\n\r\ninterface PowerShellCommandPacket extends PowerShellCommand {\r\n\r\n    /**\r\n     * The invocation mode with Net.powerShellApiInvokeCommand API.\r\n     * Default is WorkItem which uses WebSocket communication.\r\n     * Polling mode uses pooled runspaces on the gateway and the gateway will recycle and shutdown them automatically.\r\n     */\r\n    invokeMode?: 'WorkItem' | 'Polling';\r\n\r\n    /**\r\n     * Specify the initial waiting time before starting polling of result of invoke script.\r\n     */\r\n    waitTimeMs?: number;\r\n}\r\n\r\n/**\r\n * The PowerShellSession class.\r\n */\r\nexport class PowerShellSession implements Disposable {\r\n    /**\r\n     * Initializes a new instance of the PowerShellSession class.\r\n     *\r\n     * @param powerShell the PowerShell object.\r\n     * @param lifetime the disposable lifetime manager object.\r\n     */\r\n    public constructor(powerShell: PowerShell);\r\n    public constructor(powerShell: PowerShell, lifetime: DisposableLifetimeManager);\r\n    public constructor(public powerShell: PowerShell, private lifetime?: DisposableLifetimeManager) {\r\n    }\r\n\r\n    /**\r\n     * Gets the node name of session.\r\n     */\r\n    public get nodeName(): string {\r\n        return this.powerShell.nodeName;\r\n    }\r\n\r\n    /**\r\n     * Dispose the session object.\r\n     */\r\n    public dispose(): void {\r\n        if (this.lifetime) {\r\n            this.lifetime.dispose();\r\n        }\r\n    }\r\n}\r\n\r\nexport interface FallbackError extends AjaxError {\r\n    handlerError: {\r\n        message: string;\r\n        code: 'ManageAsDialogCancel';\r\n    };\r\n}\r\n\r\n/**\r\n * Class containing methods related to PowerShell runspace creation/deletion/command using PowerShell Raw API plugin.\r\n *  - It's auto holding the session as long as it's used within last 3 minutes.\r\n */\r\nexport class PowerShellRaw implements Disposable {\r\n    // 3 minutes holding time.\r\n    private static maxDeltaTimeInMs: number = 3 * 60 * 1000;\r\n    private sessionId: string;\r\n    private timestampInMs = 0;\r\n    private markDelete = false;\r\n    private internalActive = false;\r\n    private cancelPending = false;\r\n\r\n    /**\r\n     * Initializes a new instance of the PowerShellRaw class.\r\n     *\r\n     * @param nodeConnection The node connection service.\r\n     * @param context The context of PowerShell run.\r\n     */\r\n    constructor(private nodeConnection: NodeConnection, private context: PowerShellContext) {\r\n    }\r\n\r\n    /**\r\n     * Gets active status of PowerShell execution.\r\n     */\r\n    public get active(): boolean {\r\n        return this.internalActive;\r\n    }\r\n\r\n    /**\r\n     * Dispose the runspace.\r\n     */\r\n    public dispose(): void {\r\n        if (!this.active) {\r\n            // only close sessions that have been created.\r\n            // If a result was cached a component may not\r\n            // execute a command and still dispose the session\r\n            // when the component is destroyed.\r\n            if (this.sessionId) {\r\n                this.close();\r\n            }\r\n        } else {\r\n            this.markDelete = true;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Runs the given command\r\n     *\r\n     * @param command The command to execute.\r\n     * @param options the powershell options.\r\n     */\r\n    public runCommand(command: PowerShellCommand, options?: PowerShellOptions): Observable<any> {\r\n        // take the timestamp only success/healthy case.\r\n        // error session would be auto-deleted after expiration time.\r\n        this.internalActive = true;\r\n        return this.command(command, options)\r\n            .pipe(\r\n                catchError((error) => this.fallbackToJea(error, command, options)),\r\n                expand((data: any) => {\r\n                    this.timestampInMs = Date.now();\r\n                    if (this.checkCompleted(data)) {\r\n                        return EMPTY;\r\n                    }\r\n\r\n                    if (this.cancelPending) {\r\n                        // submit cancel request.\r\n                        // after set active state to false and complete the observable.\r\n                        this.cancelPending = false;\r\n                        return this.cancel()\r\n                            .pipe(\r\n                                catchError(() => {\r\n                                    this.internalActive = false;\r\n                                    return EMPTY;\r\n                                }),\r\n                                mergeMap(() => {\r\n                                    this.internalActive = false;\r\n                                    return EMPTY;\r\n                                }));\r\n                    }\r\n\r\n                    const url = Net.powerShellApiRetrieveOutput.format(this.sessionId);\r\n                    return this.nodeConnection.get(this.context.nodeName, url, <NodeRequest>this.context.requestOptions)\r\n                        .pipe(\r\n                            catchError((error) => {\r\n                                SmeWebTelemetry.tracePowershellEvent(command, TelemetryEventStates.Error, { response: error.response });\r\n                                return this.fallbackToJea(error, command, options);\r\n                            }\r\n                            ));\r\n                }));\r\n    }\r\n\r\n    /**\r\n     * Close/Delete the session / runspace.\r\n     */\r\n    public close(): void {\r\n        if (this.context.requestOptions.automatic) {\r\n            return;\r\n        }\r\n\r\n        if (this.sessionId) {\r\n            const sessionUri: string = Net.powerShellApiSessions.format(this.sessionId);\r\n            this.sessionId = null;\r\n            this.nodeConnection.deleteQuick(this.context.nodeName, sessionUri, <NodeRequest>this.context.requestOptions);\r\n            return;\r\n        }\r\n\r\n        Logging.log({\r\n            level: LogLevel.Verbose,\r\n            source: 'PowerShell/close',\r\n            message: MsftSme.getStrings<Strings>().MsftSmeShell.Core.Error.PowerShellUnableSessionClose.message\r\n        });\r\n    }\r\n\r\n    /**\r\n     * Cancel the command.\r\n     */\r\n    public cancelCommand(): Observable<any> {\r\n        if (this.internalActive) {\r\n            this.cancelPending = true;\r\n        }\r\n\r\n        return EMPTY;\r\n    }\r\n\r\n    /**\r\n     * Perform the JEA fallback, if applicable.\r\n     *\r\n     * @param error The error to handle\r\n     * @param command The command\r\n     * @param options The request options\r\n     */\r\n    private fallbackToJea(error: FallbackError, command: PowerShellCommand, options: PowerShellOptions): Observable<any> {\r\n        const authError = Net.isUnauthorized(error) || error.status === HttpStatusCode.BadRequest;\r\n        const responseEndpoint = error && error.xhr && error.xhr.getResponseHeader(headerConstants.POWERSHELL_ENDPOINT);\r\n        let requestEndpoint = (options && options.powerShellEndpoint);\r\n        requestEndpoint = requestEndpoint || (this.context.requestOptions && this.context.requestOptions.powerShellEndpoint);\r\n        const cancel = error.handlerError && error.handlerError.code && error.handlerError.code === 'ManageAsDialogCancel';\r\n        const credSSP = (options && options.authenticationMechanism === 'Credssp');\r\n\r\n        if (!cancel && authError && responseEndpoint && requestEndpoint !== responseEndpoint && !credSSP) {\r\n            this.context.requestOptions.powerShellEndpoint = responseEndpoint;\r\n            return this.command(command, options)\r\n                .pipe(tap(() => {\r\n                    // The JEA request went through - persist this context in authorization manager.\r\n                    this.nodeConnection.saveJeaContext(this.context.nodeName, responseEndpoint);\r\n                }));\r\n        }\r\n\r\n        // close on error if sessionId is available.\r\n        if (options && options.closeOnError) {\r\n            if (!this.sessionId) {\r\n                this.sessionId = error && error.xhr && error.xhr.response && error.xhr.response.sessionId;\r\n            }\r\n\r\n            if (this.sessionId) {\r\n                this.close();\r\n            }\r\n        }\r\n\r\n        this.internalActive = false;\r\n        return throwError(() => error);\r\n    }\r\n\r\n    private cancel(): Observable<any> {\r\n        if (this.sessionId && this.internalActive) {\r\n            const cancelUri: string = Net.powerShellApiCancelCommand.format(this.sessionId);\r\n            return this.nodeConnection.post(this.context.nodeName, cancelUri, null, <NodeRequest>this.context.requestOptions);\r\n        }\r\n\r\n        Logging.log({\r\n            level: LogLevel.Warning,\r\n            source: 'PowerShell',\r\n            message: MsftSme.getStrings<Strings>().MsftSmeShell.Core.Error.PowerShellUnableCancelCommand.message\r\n        });\r\n        return EMPTY;\r\n    }\r\n\r\n    /**\r\n     * Gets if timestamp was expired.\r\n     */\r\n    private get _isExpired(): boolean {\r\n        const now = Date.now();\r\n        return this.timestampInMs !== 0 && (now - this.timestampInMs) > PowerShellRaw.maxDeltaTimeInMs;\r\n    }\r\n\r\n    /**\r\n     * Initiate command execution. It auto recycles old sessions.\r\n     *\r\n     * @param command the PowerShell command.\r\n     */\r\n    private command(command: PowerShellCommand, options?: PowerShellOptions): Observable<any> {\r\n        const commandPacket: PowerShellCommandPacket = { ...command };\r\n\r\n        commandPacket.useInProcRunspace = !!options?.useInProcRunspace;\r\n\r\n        const polling = !!this.context.requestOptions.automatic;\r\n        if (polling) {\r\n            commandPacket.invokeMode = 'Polling';\r\n        }\r\n\r\n        if (options && options.waitTimeMs) {\r\n            commandPacket.waitTimeMs = options.waitTimeMs;\r\n        }\r\n\r\n        const data = Net.createPropertiesJSONString(commandPacket);\r\n        const newOptions: NodeRequestOptions = <NodeRequestOptions>{\r\n            ...this.context.requestOptions,\r\n            ...{\r\n                logAudit: options && options.logAudit,\r\n                logTelemetry: options && options.logTelemetry\r\n            }\r\n        };\r\n\r\n        const endpoint = options && options.powerShellEndpoint;\r\n        if (endpoint) {\r\n            newOptions.powerShellEndpoint = endpoint;\r\n        }\r\n\r\n        const token = options && options.authToken;\r\n        if (token) {\r\n            newOptions.authToken = token;\r\n        }\r\n\r\n        const authenticationMechanism = options && options.authenticationMechanism;\r\n        if (authenticationMechanism) {\r\n            newOptions.authenticationMechanism = authenticationMechanism;\r\n        }\r\n\r\n        if (newOptions.logTelemetry && !polling) {\r\n            SmeWebTelemetry.tracePowershellEvent(command, TelemetryEventStates.Started);\r\n        }\r\n\r\n        let commandResponse: Observable<any>;\r\n        if (polling) {\r\n            this.sessionId = null;\r\n            commandResponse = this.nodeConnection.post(this.context.nodeName, Net.powerShellApiInvokeCommand, data, <NodeRequest>newOptions);\r\n        } else if (this.sessionId == null || this._isExpired) {\r\n            this.sessionId = null;\r\n            const generatedName = this.context.key ? this.context.key + '-newSession' : 'instantSession';\r\n            const sessionUri: string = Net.powerShellApiSessions.format(generatedName);\r\n            commandResponse = this.nodeConnection.put(this.context.nodeName, sessionUri, data, <NodeRequest>newOptions);\r\n        } else {\r\n            const executeUri: string = Net.powerShellApiExecuteCommand.format(this.sessionId);\r\n            commandResponse = this.nodeConnection.post(this.context.nodeName, executeUri, data, <NodeRequest>newOptions);\r\n        }\r\n        return commandResponse.pipe(catchError((error) => {\r\n            SmeWebTelemetry.tracePowershellEvent(command, TelemetryEventStates.Error, { response: error.response });\r\n            return throwError(() => error);\r\n        }));\r\n    }\r\n\r\n    private checkCompleted(data: any): boolean {\r\n        const properties: any = Net.getItemProperties(data);\r\n        if (properties.sessionId) {\r\n            // keep the PS session GUID\r\n            this.sessionId = properties.sessionId;\r\n        }\r\n\r\n        if (properties.completed.toLowerCase() === 'true') {\r\n            this.internalActive = false;\r\n            if (this.markDelete) {\r\n                this.close();\r\n            }\r\n\r\n            return true;\r\n        }\r\n\r\n        return false;\r\n    }\r\n}\r\n\r\n/**\r\n * The PowerShell class.\r\n *\r\n * - Single instance of PowerShell class manages single runspace.\r\n * - It queues coming requests and process one at a time sequentially.\r\n * - If a command is slow and causing with multiple responses, it aggregates response into single Q result.\r\n * - A PowerShell instance should be created through create() function, and it's statically stored/managed into _map collection.\r\n * - In QueryCache operation, it can find the PowerShell instance to run PowerShell command by using find() function.\r\n * - Once all lifetime references are gone, it deletes the runspace.\r\n * - To dispose the PowerShell instance, it can use lifetime.dispose().\r\n */\r\nexport class PowerShell {\r\n    /**\r\n     * Default PowerShell endpoint.\r\n     */\r\n    public static defaultPowerShellEndpoint = 'http://schemas.microsoft.com/powershell/microsoft.powershell';\r\n\r\n    /**\r\n     * SME PowerShell endpoint.\r\n     */\r\n    public static smePowerShellEndpoint = 'http://schemas.microsoft.com/powershell/microsoft.sme.powershell';\r\n\r\n    /**\r\n     * WAC (v2) CredSSP PowerShell endpoint to control client role of CredSSP on the gateway.\r\n     */\r\n    public static wacCredSSPEndpoint = 'http://schemas.microsoft.com/powershell/Microsoft.WindowsAdminCenter.Credssp';\r\n\r\n    /**\r\n     * Static collection of PowerShell objects.\r\n     */\r\n    private static map: MsftSme.StringMap<PowerShell> = {};\r\n\r\n    /**\r\n     * Regular expression to match all the occurrences of a single quote\r\n     */\r\n    private static escapeRegex = new RegExp('\\'', 'g');\r\n\r\n    /**\r\n     * The context of PowerShell object.\r\n     */\r\n    private context: PowerShellContext;\r\n\r\n    /**\r\n     * The queue of PowerShell command requests.\r\n     */\r\n    private queue: PowerShellCommandItem[] = [];\r\n\r\n    /**\r\n     * The reference to PowerShellRaw class object.\r\n     */\r\n    private raw: PowerShellRaw;\r\n\r\n    /**\r\n     * Current data to aggregate from multiple data responses.\r\n     */\r\n    private currentData: any;\r\n\r\n    /**\r\n     * Timestamp when last command started.\r\n     */\r\n    private timestamp: number;\r\n\r\n    /**\r\n     * Create script as string.\r\n     * (Notes: Use createCommand() function which is based on PowerShell module,\r\n     *  Update gulpfile.js to generate a PowerShell module to support Show script, JEA and localization.)\r\n     *\r\n     * @param resource the script text from legacy ps-code converter.\r\n     * @param parameters the arguments.\r\n     * @param flags (optional) the switch flags.\r\n     */\r\n    public static createScript(\r\n        script: string,\r\n        parameters?: any,\r\n        flags?: string[]): string {\r\n        script = 'function cvt ($o) { return ConvertFrom-Json $o }\\n function SmeSubmit {\\n' + script + '}\\n SmeSubmit';\r\n        for (const parameter in parameters) {\r\n            if (parameters.hasOwnProperty(parameter)) {\r\n                const value = parameters[parameter];\r\n\r\n                if (value == null) {\r\n                    script += ' -{0} $null'.format(parameter);\r\n                } else {\r\n                    script += ' -{0} (cvt \\'{1}\\')'.format(\r\n                        parameter,\r\n                        JSON.stringify(value).replace(PowerShell.escapeRegex, '\\'\\''));\r\n                }\r\n            }\r\n        }\r\n\r\n        if (flags) {\r\n            for (let i = 0; i < flags.length; i++) {\r\n                script += ' -{0}'.format(flags[i]);\r\n            }\r\n        }\r\n\r\n        return script;\r\n    }\r\n\r\n    /**\r\n     * Create PowerShell request command.\r\n     * (It creates a command object of JEA PowerShell request under restricted user role environment.)\r\n     *\r\n     * @param resource the script resource object with command and script data from new ps-code converter.\r\n     * @param parameters the arguments.\r\n     * @param flags (optional) the switch flags.\r\n     * @return PowerShellCommand the PowerShell request command object.\r\n     */\r\n    public static createCommand(\r\n        resource: { command: string, script: string, module?: string },\r\n        parameters?: any,\r\n        flags?: string[],\r\n        resourceName?: string): PowerShellCommand {\r\n        // step1: Add Jea prefix dynamically\r\n        const powerShellPrefix = MsftSme.self().Init.powerShellPrefix;\r\n        let command = resource.command;\r\n        if (powerShellPrefix && resource && resource.command) {\r\n            command = PowerShell.addPowerShellPrefix(powerShellPrefix, resource.command);\r\n        }\r\n\r\n        // step2: Adding parameters converter from JSON to PowerShell.\r\n        // step3: Surround full content into SmeSubmit function.\r\n        let script = 'function cvt($o){return ConvertFrom-Json $o}\\n function SmeSubmit{\\n' + resource.script + '}\\nSmeSubmit';\r\n\r\n        // step4: Adding localized resources strings overriding Import-LocalizedData as function.\r\n        if (resourceName) {\r\n            const strings = MsftSme.getStrings<any>()[resourceName];\r\n            if (strings && strings['PowerShell']) {\r\n                const items = strings['PowerShell'];\r\n                const keys = Object.keys(items);\r\n                const rightSingleQuotationMark = '\\u2019';\r\n                const lines = keys.map(key =>\r\n                (key + '=\\''\r\n                    + items[key]\r\n                        .split(rightSingleQuotationMark)\r\n                        .join('\\'\\'')\r\n                        .split('\\'')\r\n                        .join('\\'\\'')\r\n                    + '\\'\\n'));\r\n                script = 'function Import-LocalizedData{$script:strings=@{\\n' + lines.join('') + '}}' + script;\r\n            }\r\n        }\r\n\r\n        // step5: Adding each parameter with using converter.\r\n        for (const parameter in parameters) {\r\n            if (parameters.hasOwnProperty(parameter)) {\r\n                const value = parameters[parameter];\r\n\r\n                if (value == null) {\r\n                    script += ' -{0} $null'.format(parameter);\r\n                } else {\r\n                    script += ' -{0} (cvt \\'{1}\\')'.format(\r\n                        parameter,\r\n                        JSON.stringify(value).replace(PowerShell.escapeRegex, '\\'\\''));\r\n                }\r\n            }\r\n        }\r\n\r\n        // step6: Adding switch parameters.\r\n        const flagParameters = {};\r\n        if (flags) {\r\n            for (let i = 0; i < flags.length; i++) {\r\n                script += ' -{0}'.format(flags[i]);\r\n                flagParameters[flags[i]] = true;\r\n            }\r\n        }\r\n\r\n        return <PowerShellCommand>{\r\n            module: resource.module,\r\n            command,\r\n            parameters: { ...flagParameters, ...parameters },\r\n            script,\r\n            state: 'ready'\r\n        };\r\n    }\r\n\r\n    /**\r\n     * Update the parameters in the PowerShellCommand object, and update the SmeSubmit part of the\r\n     * script with these new parameters.\r\n     *\r\n     * @param command The PowerShellCommand instance to update.\r\n     * @param parameters The new collection of parameters.\r\n     *\r\n     * Note: flags support can be added when it becomes necessary.\r\n     */\r\n     public static updateCommandParameters(command: PowerShellCommand, parameters?: any): void {\r\n        for (const parameter in parameters) {\r\n            if (parameters.hasOwnProperty(parameter)) {\r\n                command.parameters[parameter] = parameters[parameter];\r\n                const value = parameters[parameter];\r\n                // Regular expression to capture existing parameter.\r\n                const regex = new RegExp('(?<=}\\\\s+SMESubmit .*)-{0} (\\\\(cvt.+?\\\\)|\\\\$null)(?= -|$)'.format(parameter), 'i');\r\n                if (command.script.match(regex)) {\r\n                    if (value == null) {\r\n                        command.script = command.script.replace(regex, ' -{0} $null'.format(parameter));\r\n                    } else {\r\n                        command.script = command.script.replace(regex, ' -{0} (cvt \\'{1}\\')'.format(\r\n                            parameter,\r\n                            JSON.stringify(value).replace(PowerShell.escapeRegex, '\\'\\'')));\r\n                    }\r\n                } else {\r\n                    command.script += ' -{0} (cvt \\'{1}\\')'.format(\r\n                        parameter,\r\n                        JSON.stringify(value).replace(PowerShell.escapeRegex, '\\'\\''));\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Find or create new PowerShell object.\r\n     *\r\n     * @param nodeName The node to connect to.\r\n     * @param nodeConnection The node connection.\r\n     * @param key The shared key to queue the requests to use the single runspace.\r\n     * @param lifetime The lifetime container.\r\n     * @param requestOptions the options to apply to every request in this session\r\n     */\r\n    public static create(nodeName: string, nodeConnection: NodeConnection): PowerShell;\r\n    public static create(nodeName: string, nodeConnection: NodeConnection, key: string, lifetime: DisposableLifetimeManager): PowerShell;\r\n    public static create(\r\n        nodeName: string,\r\n        nodeConnection: NodeConnection,\r\n        key: string,\r\n        lifetime: DisposableLifetimeManager,\r\n        requestOptions: PowerShellSessionRequestOptions): PowerShell;\r\n    public static create(\r\n        nodeName: string,\r\n        nodeConnection: NodeConnection,\r\n        key?: string,\r\n        lifetime?: DisposableLifetimeManager,\r\n        requestOptions?: PowerShellSessionRequestOptions): PowerShell {\r\n        let ps: PowerShell;\r\n        if (key && lifetime) {\r\n            ps = PowerShell.map[PowerShell.indexName(nodeName, key)];\r\n            if (ps) {\r\n                ps.addLifetime(lifetime);\r\n                return ps;\r\n            }\r\n        }\r\n\r\n        ps = new PowerShell(nodeName, nodeConnection, key, lifetime, requestOptions);\r\n        if (key && lifetime) {\r\n            PowerShell.map[PowerShell.indexName(nodeName, key)] = ps;\r\n        }\r\n\r\n        return ps;\r\n    }\r\n\r\n    /**\r\n     * Find existing PowerShell object. Create call must be called before to create the PowerShell instance.\r\n     *\r\n     * @param nodeName The node name.\r\n     * @param key The shared key to queue the requests to use the single runspace.\r\n     */\r\n    public static find(nodeName: string, key: string): PowerShell {\r\n        return PowerShell.map[PowerShell.indexName(nodeName, key)];\r\n    }\r\n\r\n    /**\r\n     * Gets the command object from string or PowerShellCommand.\r\n     *\r\n     * @param scriptOrCommand the script string or PowerShellCommand object.\r\n     */\r\n    public static getPowerShellCommand(scriptOrCommand: string | PowerShellCommand): PowerShellCommand {\r\n        return typeof scriptOrCommand === 'string' ?\r\n            {\r\n                script: <string>scriptOrCommand,\r\n                command: null,\r\n                module: null,\r\n                state: 'ready'\r\n            }\r\n            : {\r\n                script: scriptOrCommand.script,\r\n                command: scriptOrCommand.command,\r\n                module: scriptOrCommand.module || MsftSme.self().Init.powerShellModuleName,\r\n                parameters: scriptOrCommand.parameters,\r\n                state: 'ready'\r\n            };\r\n    }\r\n\r\n    /**\r\n     * Create new options with debugging endpoint if requested.\r\n     *\r\n     * @param options the PowerShell session request options.\r\n     */\r\n    public static newEndpointOptions(options: NodeRequestOptions): NodeRequestOptions {\r\n        // if there is no endpoint but configured with powerShellEndpoint, set debugging endpoint.\r\n        const newOptions = { ...(options || {}) };\r\n        if (!newOptions.powerShellEndpoint && MsftSme.self().Init.powerShellEndpoint) {\r\n            newOptions.powerShellEndpoint = MsftSme.self().Init.powerShellEndpoint;\r\n        }\r\n\r\n        return newOptions;\r\n    }\r\n\r\n    /**\r\n     * Create the index name in map collection.\r\n     *\r\n     * @param nodeName The node name.\r\n     * @param key The shared key to queue the requests to use the single runspace.\r\n     */\r\n    private static indexName(nodeName: string, key: string): string {\r\n        return nodeName + ':' + key;\r\n    }\r\n\r\n    /**\r\n     * Adds jea prefix to the command name\r\n     *\r\n     * @param jeaPrefix The jea prefix originating from main.ts.\r\n     * @param command The powershell command to run.\r\n     */\r\n    private static addPowerShellPrefix(powerShellPrefix: string, command: string): string {\r\n        const hyphenSeparatorIndex = command.indexOf('-');\r\n        const verb = command.substring(0, hyphenSeparatorIndex);\r\n        const target = command.substring(hyphenSeparatorIndex + 1);\r\n        if (target.indexOf(powerShellPrefix) === 0) {\r\n            throw new Error('Command already contains prefix');\r\n        }\r\n\r\n        return verb + '-' + powerShellPrefix + target;\r\n    }\r\n\r\n    /**\r\n     * Initializes a new instance of the PowerShell class.\r\n     * (private constructor which shouldn't be called directly.)\r\n     *\r\n     * @param nodeConnection The node connection service.\r\n     * @param key The shared key to queue the requests to use the single runspace.\r\n     * @param lifetime The lifetime container.\r\n     */\r\n    constructor(\r\n        nodeName: string,\r\n        nodeConnection: NodeConnection,\r\n        key: string,\r\n        lifetime: DisposableLifetimeManager,\r\n        options: PowerShellSessionRequestOptions) {\r\n        this.context = {\r\n            nodeName: nodeName,\r\n            key: key,\r\n            lifetimes: [],\r\n            requestOptions: PowerShell.newEndpointOptions(options)\r\n        };\r\n        this.timestamp = 0;\r\n        this.raw = new PowerShellRaw(nodeConnection, this.context);\r\n        if (key && lifetime) {\r\n            lifetime.registerForDispose(new Disposer(() => this.lifetimeDisposer(lifetime)));\r\n            this.context.lifetimes.push(lifetime);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Gets node name from current context.\r\n     */\r\n    public get nodeName(): string {\r\n        return this.context.nodeName;\r\n    }\r\n\r\n    /**\r\n     * Run PowerShell command.\r\n     *\r\n     * @param command The command.\r\n     * @param options The options.\r\n     * @return PromiseV The result of PowerShell command.\r\n     */\r\n    public run(scriptOrCommand: string | PowerShellCommand, options?: PowerShellOptions): Observable<any> {\r\n        const command = PowerShell.getPowerShellCommand(scriptOrCommand);\r\n        if (this.context.lifetimes.length === 0) {\r\n            // no disposer is assigned, force to close the session after every query.\r\n            const timeoutMs: number = options && options.timeoutMs;\r\n\r\n            if (options) {\r\n                options.timeoutMs = timeoutMs;\r\n                options.close = true;\r\n            } else {\r\n                options = { timeoutMs: timeoutMs, close: true };\r\n            }\r\n        }\r\n\r\n        // queue the request.\r\n        const observable = this.enqueue(command, options);\r\n        return observable;\r\n    }\r\n\r\n    /**\r\n     * Cancel PowerShell command.\r\n     */\r\n    public cancel(): Observable<any> {\r\n        return this.raw.cancelCommand();\r\n    }\r\n\r\n    /**\r\n     * Enqueue a command request.\r\n     *\r\n     * @param command The command.\r\n     * @param options The options.\r\n     */\r\n    private enqueue(command: PowerShellCommand, options?: PowerShellOptions): Observable<any> {\r\n        return new Observable((observer: Observer<any>) => {\r\n            this.queue.push(<PowerShellCommandItem>{ observer, command, options });\r\n            this.dequeue();\r\n        });\r\n    }\r\n\r\n    /**\r\n     * Dequeue a command request.\r\n     */\r\n    private dequeue(): boolean {\r\n        if (this.raw.active) {\r\n            return false;\r\n        }\r\n\r\n        const item: PowerShellCommandItem = this.queue.shift();\r\n        if (item) {\r\n            this.currentData = null;\r\n            this.timestamp = Date.now();\r\n            this.raw.runCommand(item.command, item.options).subscribe({\r\n                next: data => {\r\n                    const properties: any = Net.getItemProperties(data);\r\n                    this.collect(\r\n                        properties,\r\n                        item.options && item.options.timeoutMs,\r\n                        item.options && item.options.partial ? item.observer : null);\r\n                },\r\n                error: error => {\r\n                    if (item.options && item.options.close) {\r\n                        this.raw.close();\r\n                    }\r\n\r\n                    item.observer.error(error);\r\n                    this.timestamp = 0;\r\n                    this.dequeue();\r\n                },\r\n                complete: () => {\r\n                    if (item.options && item.options.close) {\r\n                        this.raw.close();\r\n                    }\r\n\r\n                    if (!item.options || !item.options.partial) {\r\n                        item.observer.next(this.currentData);\r\n                    }\r\n\r\n                    item.observer.complete();\r\n                    this.timestamp = 0;\r\n                    this.dequeue();\r\n                }\r\n            });\r\n            return true;\r\n        }\r\n\r\n        return false;\r\n    }\r\n\r\n    /**\r\n     * Collect response result and aggregate into single object.\r\n     *\r\n     * @param properties The properties of response object.\r\n     * @param timeoutMs The timeout to cancel command.\r\n     * @param observer The observer of powershell results.\r\n     */\r\n    private collect(properties: any, timeoutMs: number, observer: Observer<any>): void {\r\n        if (timeoutMs && this.timestamp && (Date.now() - this.timestamp > timeoutMs)) {\r\n            // force to cancel the command because of unexpected longer execution.\r\n            this.raw.cancelCommand();\r\n            this.timestamp = 0;\r\n            return;\r\n        }\r\n\r\n        if (observer) {\r\n            // return partial data if observer is not null.\r\n            observer.next(properties);\r\n            this.currentData = properties;\r\n            return;\r\n        }\r\n\r\n        if (this.currentData != null && this.currentData.results && properties.results) {\r\n            let array: any[];\r\n            if (MsftSme.getTypeOf(this.currentData.results) === 'array') {\r\n                array = this.currentData.results;\r\n            } else {\r\n                array = [this.currentData.results];\r\n            }\r\n\r\n            if (MsftSme.getTypeOf(properties.results) === 'array') {\r\n                properties.results.forEach((x: any) => {\r\n                    array.push(x);\r\n                });\r\n            } else {\r\n                array.push(properties.results);\r\n            }\r\n\r\n            this.currentData.results = array;\r\n            return;\r\n        }\r\n\r\n        this.currentData = properties;\r\n    }\r\n\r\n    /**\r\n     * Attach lifetime object to disposer when disposing.\r\n     *\r\n     * @param lifetime The lifetime object.\r\n     */\r\n    private addLifetime(lifetime: DisposableLifetimeManager): void {\r\n        const found: DisposableLifetimeManager = MsftSme.find(\r\n            this.context.lifetimes, (value: DisposableLifetimeManager) => value === lifetime);\r\n        if (!found) {\r\n            this.context.lifetimes.push(lifetime);\r\n            lifetime.registerForDispose(new Disposer(() => this.lifetimeDisposer(lifetime)));\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Callback when disposing the container of view model.\r\n     * If none, reference the PowerShell object. Dispose it. (Delete runspace)\r\n     *\r\n     * @param lifetime The lifetime object.\r\n     */\r\n    private lifetimeDisposer(lifetime: DisposableLifetimeManager): void {\r\n        const found: DisposableLifetimeManager = MsftSme.find(\r\n            this.context.lifetimes, (value: DisposableLifetimeManager) => value === lifetime);\r\n        if (found) {\r\n            MsftSme.remove(this.context.lifetimes, lifetime);\r\n            if (this.context.lifetimes.length === 0) {\r\n                // cancel queue command requests.\r\n                this.queue.forEach((value: PowerShellCommandItem) => {\r\n                    value.observer.next(null);\r\n                    value.observer.complete();\r\n                });\r\n\r\n                // delete from the map collection and delete the runspace/session.\r\n                delete PowerShell.map[PowerShell.indexName(this.context.nodeName, this.context.key)];\r\n                this.raw.dispose();\r\n            }\r\n        }\r\n    }\r\n}\r\n"]}