| "use strict";
|
| var __importDefault = (this && this.__importDefault) || function (mod) {
|
| return (mod && mod.__esModule) ? mod : { "default": mod };
|
| };
|
| Object.defineProperty(exports, "__esModule", { value: true });
|
| exports.StdioClientTransport = exports.DEFAULT_INHERITED_ENV_VARS = void 0;
|
| exports.getDefaultEnvironment = getDefaultEnvironment;
|
| const cross_spawn_1 = __importDefault(require("cross-spawn"));
|
| const node_process_1 = __importDefault(require("node:process"));
|
| const node_stream_1 = require("node:stream");
|
| const stdio_js_1 = require("../shared/stdio.js");
|
| |
| |
|
|
| exports.DEFAULT_INHERITED_ENV_VARS = node_process_1.default.platform === 'win32'
|
| ? [
|
| 'APPDATA',
|
| 'HOMEDRIVE',
|
| 'HOMEPATH',
|
| 'LOCALAPPDATA',
|
| 'PATH',
|
| 'PROCESSOR_ARCHITECTURE',
|
| 'SYSTEMDRIVE',
|
| 'SYSTEMROOT',
|
| 'TEMP',
|
| 'USERNAME',
|
| 'USERPROFILE',
|
| 'PROGRAMFILES'
|
| ]
|
| :
|
| ['HOME', 'LOGNAME', 'PATH', 'SHELL', 'TERM', 'USER'];
|
| |
| |
|
|
| function getDefaultEnvironment() {
|
| const env = {};
|
| for (const key of exports.DEFAULT_INHERITED_ENV_VARS) {
|
| const value = node_process_1.default.env[key];
|
| if (value === undefined) {
|
| continue;
|
| }
|
| if (value.startsWith('()')) {
|
|
|
| continue;
|
| }
|
| env[key] = value;
|
| }
|
| return env;
|
| }
|
| |
| |
| |
| |
|
|
| class StdioClientTransport {
|
| constructor(server) {
|
| this._readBuffer = new stdio_js_1.ReadBuffer();
|
| this._stderrStream = null;
|
| this._serverParams = server;
|
| if (server.stderr === 'pipe' || server.stderr === 'overlapped') {
|
| this._stderrStream = new node_stream_1.PassThrough();
|
| }
|
| }
|
| |
| |
|
|
| async start() {
|
| if (this._process) {
|
| throw new Error('StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.');
|
| }
|
| return new Promise((resolve, reject) => {
|
| this._process = (0, cross_spawn_1.default)(this._serverParams.command, this._serverParams.args ?? [], {
|
|
|
| env: {
|
| ...getDefaultEnvironment(),
|
| ...this._serverParams.env
|
| },
|
| stdio: ['pipe', 'pipe', this._serverParams.stderr ?? 'inherit'],
|
| shell: false,
|
| windowsHide: node_process_1.default.platform === 'win32',
|
| cwd: this._serverParams.cwd
|
| });
|
| this._process.on('error', error => {
|
| reject(error);
|
| this.onerror?.(error);
|
| });
|
| this._process.on('spawn', () => {
|
| resolve();
|
| });
|
| this._process.on('close', _code => {
|
| this._process = undefined;
|
| this.onclose?.();
|
| });
|
| this._process.stdin?.on('error', error => {
|
| this.onerror?.(error);
|
| });
|
| this._process.stdout?.on('data', chunk => {
|
| this._readBuffer.append(chunk);
|
| this.processReadBuffer();
|
| });
|
| this._process.stdout?.on('error', error => {
|
| this.onerror?.(error);
|
| });
|
| if (this._stderrStream && this._process.stderr) {
|
| this._process.stderr.pipe(this._stderrStream);
|
| }
|
| });
|
| }
|
| |
| |
| |
| |
| |
| |
|
|
| get stderr() {
|
| if (this._stderrStream) {
|
| return this._stderrStream;
|
| }
|
| return this._process?.stderr ?? null;
|
| }
|
| |
| |
| |
| |
|
|
| get pid() {
|
| return this._process?.pid ?? null;
|
| }
|
| processReadBuffer() {
|
| while (true) {
|
| try {
|
| const message = this._readBuffer.readMessage();
|
| if (message === null) {
|
| break;
|
| }
|
| this.onmessage?.(message);
|
| }
|
| catch (error) {
|
| this.onerror?.(error);
|
| }
|
| }
|
| }
|
| async close() {
|
| if (this._process) {
|
| const processToClose = this._process;
|
| this._process = undefined;
|
| const closePromise = new Promise(resolve => {
|
| processToClose.once('close', () => {
|
| resolve();
|
| });
|
| });
|
| try {
|
| processToClose.stdin?.end();
|
| }
|
| catch {
|
|
|
| }
|
| await Promise.race([closePromise, new Promise(resolve => setTimeout(resolve, 2000).unref())]);
|
| if (processToClose.exitCode === null) {
|
| try {
|
| processToClose.kill('SIGTERM');
|
| }
|
| catch {
|
|
|
| }
|
| await Promise.race([closePromise, new Promise(resolve => setTimeout(resolve, 2000).unref())]);
|
| }
|
| if (processToClose.exitCode === null) {
|
| try {
|
| processToClose.kill('SIGKILL');
|
| }
|
| catch {
|
|
|
| }
|
| }
|
| }
|
| this._readBuffer.clear();
|
| }
|
| send(message) {
|
| return new Promise(resolve => {
|
| if (!this._process?.stdin) {
|
| throw new Error('Not connected');
|
| }
|
| const json = (0, stdio_js_1.serializeMessage)(message);
|
| if (this._process.stdin.write(json)) {
|
| resolve();
|
| }
|
| else {
|
| this._process.stdin.once('drain', resolve);
|
| }
|
| });
|
| }
|
| }
|
| exports.StdioClientTransport = StdioClientTransport;
|
| |