test
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
mol
2024-07-06 22:23:31 +08:00
parent 08173d8497
commit 263cb5ef03
1663 changed files with 526884 additions and 0 deletions

View File

@ -0,0 +1,19 @@
"use strict";
/**
* Copyright (c) 2019, Microsoft Corporation (MIT License).
*
* This module fetches the console process list for a particular PID. It must be
* called from a different process (child_process.fork) as there can only be a
* single console attached to a process.
*/
var getConsoleProcessList;
try {
getConsoleProcessList = require('../build/Release/conpty_console_list.node').getConsoleProcessList;
}
catch (err) {
getConsoleProcessList = require('../build/Debug/conpty_console_list.node').getConsoleProcessList;
}
var shellPid = parseInt(process.argv[2], 10);
var consoleProcessList = getConsoleProcessList(shellPid);
process.send({ consoleProcessList: consoleProcessList });
process.exit(0);

View File

@ -0,0 +1,46 @@
"use strict";
/**
* Copyright (c) 2019, Microsoft Corporation (MIT License).
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventEmitter2 = void 0;
var EventEmitter2 = /** @class */ (function () {
function EventEmitter2() {
this._listeners = [];
}
Object.defineProperty(EventEmitter2.prototype, "event", {
get: function () {
var _this = this;
if (!this._event) {
this._event = function (listener) {
_this._listeners.push(listener);
var disposable = {
dispose: function () {
for (var i = 0; i < _this._listeners.length; i++) {
if (_this._listeners[i] === listener) {
_this._listeners.splice(i, 1);
return;
}
}
}
};
return disposable;
};
}
return this._event;
},
enumerable: false,
configurable: true
});
EventEmitter2.prototype.fire = function (data) {
var queue = [];
for (var i = 0; i < this._listeners.length; i++) {
queue.push(this._listeners[i]);
}
for (var i = 0; i < queue.length; i++) {
queue[i].call(undefined, data);
}
};
return EventEmitter2;
}());
exports.EventEmitter2 = EventEmitter2;

View File

@ -0,0 +1,50 @@
"use strict";
/**
* Copyright (c) 2012-2015, Christopher Jeffrey, Peter Sunde (MIT License)
* Copyright (c) 2016, Daniel Imms (MIT License).
* Copyright (c) 2018, Microsoft Corporation (MIT License).
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.native = exports.open = exports.createTerminal = exports.fork = exports.spawn = void 0;
var terminalCtor;
if (process.platform === 'win32') {
terminalCtor = require('./windowsTerminal').WindowsTerminal;
}
else {
terminalCtor = require('./unixTerminal').UnixTerminal;
}
/**
* Forks a process as a pseudoterminal.
* @param file The file to launch.
* @param args The file's arguments as argv (string[]) or in a pre-escaped
* CommandLine format (string). Note that the CommandLine option is only
* available on Windows and is expected to be escaped properly.
* @param options The options of the terminal.
* @throws When the file passed to spawn with does not exists.
* @see CommandLineToArgvW https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx
* @see Parsing C++ Comamnd-Line Arguments https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
* @see GetCommandLine https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156.aspx
*/
function spawn(file, args, opt) {
return new terminalCtor(file, args, opt);
}
exports.spawn = spawn;
/** @deprecated */
function fork(file, args, opt) {
return new terminalCtor(file, args, opt);
}
exports.fork = fork;
/** @deprecated */
function createTerminal(file, args, opt) {
return new terminalCtor(file, args, opt);
}
exports.createTerminal = createTerminal;
function open(options) {
return terminalCtor.open(options);
}
exports.open = open;
/**
* Expose the native API when not Windows, note that this is not public API and
* could be removed at any time.
*/
exports.native = (process.platform !== 'win32' ? require('../build/Release/pty.node') : null);

View File

@ -0,0 +1,6 @@
"use strict";
/**
* Copyright (c) 2016, Daniel Imms (MIT License).
* Copyright (c) 2018, Microsoft Corporation (MIT License).
*/
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,10 @@
"use strict";
/**
* Copyright (c) 2020, Microsoft Corporation (MIT License).
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getWorkerPipeName = void 0;
function getWorkerPipeName(conoutPipeName) {
return conoutPipeName + "-worker";
}
exports.getWorkerPipeName = getWorkerPipeName;

View File

@ -0,0 +1,189 @@
"use strict";
/**
* Copyright (c) 2012-2015, Christopher Jeffrey (MIT License)
* Copyright (c) 2016, Daniel Imms (MIT License).
* Copyright (c) 2018, Microsoft Corporation (MIT License).
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Terminal = exports.DEFAULT_ROWS = exports.DEFAULT_COLS = void 0;
var events_1 = require("events");
var eventEmitter2_1 = require("./eventEmitter2");
exports.DEFAULT_COLS = 80;
exports.DEFAULT_ROWS = 24;
/**
* Default messages to indicate PAUSE/RESUME for automatic flow control.
* To avoid conflicts with rebound XON/XOFF control codes (such as on-my-zsh),
* the sequences can be customized in `IPtyForkOptions`.
*/
var FLOW_CONTROL_PAUSE = '\x13'; // defaults to XOFF
var FLOW_CONTROL_RESUME = '\x11'; // defaults to XON
var Terminal = /** @class */ (function () {
function Terminal(opt) {
this._pid = 0;
this._fd = 0;
this._cols = 0;
this._rows = 0;
this._readable = false;
this._writable = false;
this._onData = new eventEmitter2_1.EventEmitter2();
this._onExit = new eventEmitter2_1.EventEmitter2();
// for 'close'
this._internalee = new events_1.EventEmitter();
// setup flow control handling
this.handleFlowControl = !!(opt === null || opt === void 0 ? void 0 : opt.handleFlowControl);
this._flowControlPause = (opt === null || opt === void 0 ? void 0 : opt.flowControlPause) || FLOW_CONTROL_PAUSE;
this._flowControlResume = (opt === null || opt === void 0 ? void 0 : opt.flowControlResume) || FLOW_CONTROL_RESUME;
if (!opt) {
return;
}
// Do basic type checks here in case node-pty is being used within JavaScript. If the wrong
// types go through to the C++ side it can lead to hard to diagnose exceptions.
this._checkType('name', opt.name ? opt.name : undefined, 'string');
this._checkType('cols', opt.cols ? opt.cols : undefined, 'number');
this._checkType('rows', opt.rows ? opt.rows : undefined, 'number');
this._checkType('cwd', opt.cwd ? opt.cwd : undefined, 'string');
this._checkType('env', opt.env ? opt.env : undefined, 'object');
this._checkType('uid', opt.uid ? opt.uid : undefined, 'number');
this._checkType('gid', opt.gid ? opt.gid : undefined, 'number');
this._checkType('encoding', opt.encoding ? opt.encoding : undefined, 'string');
}
Object.defineProperty(Terminal.prototype, "onData", {
get: function () { return this._onData.event; },
enumerable: false,
configurable: true
});
Object.defineProperty(Terminal.prototype, "onExit", {
get: function () { return this._onExit.event; },
enumerable: false,
configurable: true
});
Object.defineProperty(Terminal.prototype, "pid", {
get: function () { return this._pid; },
enumerable: false,
configurable: true
});
Object.defineProperty(Terminal.prototype, "cols", {
get: function () { return this._cols; },
enumerable: false,
configurable: true
});
Object.defineProperty(Terminal.prototype, "rows", {
get: function () { return this._rows; },
enumerable: false,
configurable: true
});
Terminal.prototype.write = function (data) {
if (this.handleFlowControl) {
// PAUSE/RESUME messages are not forwarded to the pty
if (data === this._flowControlPause) {
this.pause();
return;
}
if (data === this._flowControlResume) {
this.resume();
return;
}
}
// everything else goes to the real pty
this._write(data);
};
Terminal.prototype._forwardEvents = function () {
var _this = this;
this.on('data', function (e) { return _this._onData.fire(e); });
this.on('exit', function (exitCode, signal) { return _this._onExit.fire({ exitCode: exitCode, signal: signal }); });
};
Terminal.prototype._checkType = function (name, value, type, allowArray) {
if (allowArray === void 0) { allowArray = false; }
if (value === undefined) {
return;
}
if (allowArray) {
if (Array.isArray(value)) {
value.forEach(function (v, i) {
if (typeof v !== type) {
throw new Error(name + "[" + i + "] must be a " + type + " (not a " + typeof v[i] + ")");
}
});
return;
}
}
if (typeof value !== type) {
throw new Error(name + " must be a " + type + " (not a " + typeof value + ")");
}
};
/** See net.Socket.end */
Terminal.prototype.end = function (data) {
this._socket.end(data);
};
/** See stream.Readable.pipe */
Terminal.prototype.pipe = function (dest, options) {
return this._socket.pipe(dest, options);
};
/** See net.Socket.pause */
Terminal.prototype.pause = function () {
return this._socket.pause();
};
/** See net.Socket.resume */
Terminal.prototype.resume = function () {
return this._socket.resume();
};
/** See net.Socket.setEncoding */
Terminal.prototype.setEncoding = function (encoding) {
if (this._socket._decoder) {
delete this._socket._decoder;
}
if (encoding) {
this._socket.setEncoding(encoding);
}
};
Terminal.prototype.addListener = function (eventName, listener) { this.on(eventName, listener); };
Terminal.prototype.on = function (eventName, listener) {
if (eventName === 'close') {
this._internalee.on('close', listener);
return;
}
this._socket.on(eventName, listener);
};
Terminal.prototype.emit = function (eventName) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
if (eventName === 'close') {
return this._internalee.emit.apply(this._internalee, arguments);
}
return this._socket.emit.apply(this._socket, arguments);
};
Terminal.prototype.listeners = function (eventName) {
return this._socket.listeners(eventName);
};
Terminal.prototype.removeListener = function (eventName, listener) {
this._socket.removeListener(eventName, listener);
};
Terminal.prototype.removeAllListeners = function (eventName) {
this._socket.removeAllListeners(eventName);
};
Terminal.prototype.once = function (eventName, listener) {
this._socket.once(eventName, listener);
};
Terminal.prototype._close = function () {
this._socket.readable = false;
this.write = function () { };
this.end = function () { };
this._writable = false;
this._readable = false;
};
Terminal.prototype._parseEnv = function (env) {
var keys = Object.keys(env || {});
var pairs = [];
for (var i = 0; i < keys.length; i++) {
if (keys[i] === undefined) {
continue;
}
pairs.push(keys[i] + '=' + env[keys[i]]);
}
return pairs;
};
return Terminal;
}());
exports.Terminal = Terminal;

View File

@ -0,0 +1,6 @@
"use strict";
/**
* Copyright (c) 2017, Daniel Imms (MIT License).
* Copyright (c) 2018, Microsoft Corporation (MIT License).
*/
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,304 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnixTerminal = void 0;
/**
* Copyright (c) 2012-2015, Christopher Jeffrey (MIT License)
* Copyright (c) 2016, Daniel Imms (MIT License).
* Copyright (c) 2018, Microsoft Corporation (MIT License).
*/
var net = require("net");
var path = require("path");
var terminal_1 = require("./terminal");
var utils_1 = require("./utils");
var pty;
var helperPath;
try {
pty = require('../build/Release/pty.node');
helperPath = '../build/Release/spawn-helper';
}
catch (outerError) {
try {
pty = require('../build/Debug/pty.node');
helperPath = '../build/Debug/spawn-helper';
}
catch (innerError) {
console.error('innerError', innerError);
// Re-throw the exception from the Release require if the Debug require fails as well
throw outerError;
}
}
helperPath = path.resolve(__dirname, helperPath);
helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
var DEFAULT_FILE = 'sh';
var DEFAULT_NAME = 'xterm';
var DESTROY_SOCKET_TIMEOUT_MS = 200;
var UnixTerminal = /** @class */ (function (_super) {
__extends(UnixTerminal, _super);
function UnixTerminal(file, args, opt) {
var _a, _b;
var _this = _super.call(this, opt) || this;
_this._boundClose = false;
_this._emittedClose = false;
if (typeof args === 'string') {
throw new Error('args as a string is not supported on unix.');
}
// Initialize arguments
args = args || [];
file = file || DEFAULT_FILE;
opt = opt || {};
opt.env = opt.env || process.env;
_this._cols = opt.cols || terminal_1.DEFAULT_COLS;
_this._rows = opt.rows || terminal_1.DEFAULT_ROWS;
var uid = (_a = opt.uid) !== null && _a !== void 0 ? _a : -1;
var gid = (_b = opt.gid) !== null && _b !== void 0 ? _b : -1;
var env = utils_1.assign({}, opt.env);
if (opt.env === process.env) {
_this._sanitizeEnv(env);
}
var cwd = opt.cwd || process.cwd();
env.PWD = cwd;
var name = opt.name || env.TERM || DEFAULT_NAME;
env.TERM = name;
var parsedEnv = _this._parseEnv(env);
var encoding = (opt.encoding === undefined ? 'utf8' : opt.encoding);
var onexit = function (code, signal) {
// XXX Sometimes a data event is emitted after exit. Wait til socket is
// destroyed.
if (!_this._emittedClose) {
if (_this._boundClose) {
return;
}
_this._boundClose = true;
// From macOS High Sierra 10.13.2 sometimes the socket never gets
// closed. A timeout is applied here to avoid the terminal never being
// destroyed when this occurs.
var timeout_1 = setTimeout(function () {
timeout_1 = null;
// Destroying the socket now will cause the close event to fire
_this._socket.destroy();
}, DESTROY_SOCKET_TIMEOUT_MS);
_this.once('close', function () {
if (timeout_1 !== null) {
clearTimeout(timeout_1);
}
_this.emit('exit', code, signal);
});
return;
}
_this.emit('exit', code, signal);
};
// fork
var term = pty.fork(file, args, parsedEnv, cwd, _this._cols, _this._rows, uid, gid, (encoding === 'utf8'), helperPath, onexit);
_this._socket = new PipeSocket(term.fd);
if (encoding !== null) {
_this._socket.setEncoding(encoding);
}
// setup
_this._socket.on('error', function (err) {
// NOTE: fs.ReadStream gets EAGAIN twice at first:
if (err.code) {
if (~err.code.indexOf('EAGAIN')) {
return;
}
}
// close
_this._close();
// EIO on exit from fs.ReadStream:
if (!_this._emittedClose) {
_this._emittedClose = true;
_this.emit('close');
}
// EIO, happens when someone closes our child process: the only process in
// the terminal.
// node < 0.6.14: errno 5
// node >= 0.6.14: read EIO
if (err.code) {
if (~err.code.indexOf('errno 5') || ~err.code.indexOf('EIO')) {
return;
}
}
// throw anything else
if (_this.listeners('error').length < 2) {
throw err;
}
});
_this._pid = term.pid;
_this._fd = term.fd;
_this._pty = term.pty;
_this._file = file;
_this._name = name;
_this._readable = true;
_this._writable = true;
_this._socket.on('close', function () {
if (_this._emittedClose) {
return;
}
_this._emittedClose = true;
_this._close();
_this.emit('close');
});
_this._forwardEvents();
return _this;
}
Object.defineProperty(UnixTerminal.prototype, "master", {
get: function () { return this._master; },
enumerable: false,
configurable: true
});
Object.defineProperty(UnixTerminal.prototype, "slave", {
get: function () { return this._slave; },
enumerable: false,
configurable: true
});
UnixTerminal.prototype._write = function (data) {
this._socket.write(data);
};
Object.defineProperty(UnixTerminal.prototype, "fd", {
/* Accessors */
get: function () { return this._fd; },
enumerable: false,
configurable: true
});
Object.defineProperty(UnixTerminal.prototype, "ptsName", {
get: function () { return this._pty; },
enumerable: false,
configurable: true
});
/**
* openpty
*/
UnixTerminal.open = function (opt) {
var self = Object.create(UnixTerminal.prototype);
opt = opt || {};
if (arguments.length > 1) {
opt = {
cols: arguments[1],
rows: arguments[2]
};
}
var cols = opt.cols || terminal_1.DEFAULT_COLS;
var rows = opt.rows || terminal_1.DEFAULT_ROWS;
var encoding = (opt.encoding === undefined ? 'utf8' : opt.encoding);
// open
var term = pty.open(cols, rows);
self._master = new PipeSocket(term.master);
if (encoding !== null) {
self._master.setEncoding(encoding);
}
self._master.resume();
self._slave = new PipeSocket(term.slave);
if (encoding !== null) {
self._slave.setEncoding(encoding);
}
self._slave.resume();
self._socket = self._master;
self._pid = -1;
self._fd = term.master;
self._pty = term.pty;
self._file = process.argv[0] || 'node';
self._name = process.env.TERM || '';
self._readable = true;
self._writable = true;
self._socket.on('error', function (err) {
self._close();
if (self.listeners('error').length < 2) {
throw err;
}
});
self._socket.on('close', function () {
self._close();
});
return self;
};
UnixTerminal.prototype.destroy = function () {
var _this = this;
this._close();
// Need to close the read stream so node stops reading a dead file
// descriptor. Then we can safely SIGHUP the shell.
this._socket.once('close', function () {
_this.kill('SIGHUP');
});
this._socket.destroy();
};
UnixTerminal.prototype.kill = function (signal) {
try {
process.kill(this.pid, signal || 'SIGHUP');
}
catch (e) { /* swallow */ }
};
Object.defineProperty(UnixTerminal.prototype, "process", {
/**
* Gets the name of the process.
*/
get: function () {
if (process.platform === 'darwin') {
var title = pty.process(this._fd);
return (title !== 'kernel_task') ? title : this._file;
}
return pty.process(this._fd, this._pty) || this._file;
},
enumerable: false,
configurable: true
});
/**
* TTY
*/
UnixTerminal.prototype.resize = function (cols, rows) {
if (cols <= 0 || rows <= 0 || isNaN(cols) || isNaN(rows) || cols === Infinity || rows === Infinity) {
throw new Error('resizing must be done using positive cols and rows');
}
pty.resize(this._fd, cols, rows);
this._cols = cols;
this._rows = rows;
};
UnixTerminal.prototype.clear = function () {
};
UnixTerminal.prototype._sanitizeEnv = function (env) {
// Make sure we didn't start our server from inside tmux.
delete env['TMUX'];
delete env['TMUX_PANE'];
// Make sure we didn't start our server from inside screen.
// http://web.mit.edu/gnu/doc/html/screen_20.html
delete env['STY'];
delete env['WINDOW'];
// Delete some variables that might confuse our terminal.
delete env['WINDOWID'];
delete env['TERMCAP'];
delete env['COLUMNS'];
delete env['LINES'];
};
return UnixTerminal;
}(terminal_1.Terminal));
exports.UnixTerminal = UnixTerminal;
/**
* Wraps net.Socket to force the handle type "PIPE" by temporarily overwriting
* tty_wrap.guessHandleType.
* See: https://github.com/chjj/pty.js/issues/103
*/
var PipeSocket = /** @class */ (function (_super) {
__extends(PipeSocket, _super);
function PipeSocket(fd) {
var _this = this;
var pipeWrap = process.binding('pipe_wrap'); // tslint:disable-line
// @types/node has fd as string? https://github.com/DefinitelyTyped/DefinitelyTyped/pull/18275
var handle = new pipeWrap.Pipe(pipeWrap.constants.SOCKET);
handle.open(fd);
_this = _super.call(this, { handle: handle }) || this;
return _this;
}
return PipeSocket;
}(net.Socket));

View File

@ -0,0 +1,16 @@
"use strict";
/**
* Copyright (c) 2017, Daniel Imms (MIT License).
* Copyright (c) 2018, Microsoft Corporation (MIT License).
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.assign = void 0;
function assign(target) {
var sources = [];
for (var _i = 1; _i < arguments.length; _i++) {
sources[_i - 1] = arguments[_i];
}
sources.forEach(function (source) { return Object.keys(source).forEach(function (key) { return target[key] = source[key]; }); });
return target;
}
exports.assign = assign;

View File

@ -0,0 +1,121 @@
"use strict";
/**
* Copyright (c) 2020, Microsoft Corporation (MIT License).
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConoutConnection = void 0;
var worker_threads_1 = require("worker_threads");
var conout_1 = require("./shared/conout");
var path_1 = require("path");
var eventEmitter2_1 = require("./eventEmitter2");
/**
* The amount of time to wait for additional data after the conpty shell process has exited before
* shutting down the worker and sockets. The timer will be reset if a new data event comes in after
* the timer has started.
*/
var FLUSH_DATA_INTERVAL = 1000;
/**
* Connects to and manages the lifecycle of the conout socket. This socket must be drained on
* another thread in order to avoid deadlocks where Conpty waits for the out socket to drain
* when `ClosePseudoConsole` is called. This happens when data is being written to the terminal when
* the pty is closed.
*
* See also:
* - https://github.com/microsoft/node-pty/issues/375
* - https://github.com/microsoft/vscode/issues/76548
* - https://github.com/microsoft/terminal/issues/1810
* - https://docs.microsoft.com/en-us/windows/console/closepseudoconsole
*/
var ConoutConnection = /** @class */ (function () {
function ConoutConnection(_conoutPipeName) {
var _this = this;
this._conoutPipeName = _conoutPipeName;
this._isDisposed = false;
this._onReady = new eventEmitter2_1.EventEmitter2();
var workerData = { conoutPipeName: _conoutPipeName };
var scriptPath = __dirname.replace('node_modules.asar', 'node_modules.asar.unpacked');
this._worker = new worker_threads_1.Worker(path_1.join(scriptPath, 'worker/conoutSocketWorker.js'), { workerData: workerData });
this._worker.on('message', function (message) {
switch (message) {
case 1 /* READY */:
_this._onReady.fire();
return;
default:
console.warn('Unexpected ConoutWorkerMessage', message);
}
});
}
Object.defineProperty(ConoutConnection.prototype, "onReady", {
get: function () { return this._onReady.event; },
enumerable: false,
configurable: true
});
ConoutConnection.prototype.dispose = function () {
if (this._isDisposed) {
return;
}
this._isDisposed = true;
// Drain all data from the socket before closing
this._drainDataAndClose();
};
ConoutConnection.prototype.connectSocket = function (socket) {
socket.connect(conout_1.getWorkerPipeName(this._conoutPipeName));
};
ConoutConnection.prototype._drainDataAndClose = function () {
var _this = this;
if (this._drainTimeout) {
clearTimeout(this._drainTimeout);
}
this._drainTimeout = setTimeout(function () { return _this._destroySocket(); }, FLUSH_DATA_INTERVAL);
};
ConoutConnection.prototype._destroySocket = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this._worker.terminate()];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
return ConoutConnection;
}());
exports.ConoutConnection = ConoutConnection;

View File

@ -0,0 +1,322 @@
"use strict";
/**
* Copyright (c) 2012-2015, Christopher Jeffrey, Peter Sunde (MIT License)
* Copyright (c) 2016, Daniel Imms (MIT License).
* Copyright (c) 2018, Microsoft Corporation (MIT License).
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.argsToCommandLine = exports.WindowsPtyAgent = void 0;
var fs = require("fs");
var os = require("os");
var path = require("path");
var net_1 = require("net");
var child_process_1 = require("child_process");
var windowsConoutConnection_1 = require("./windowsConoutConnection");
var conptyNative;
var winptyNative;
/**
* The amount of time to wait for additional data after the conpty shell process has exited before
* shutting down the socket. The timer will be reset if a new data event comes in after the timer
* has started.
*/
var FLUSH_DATA_INTERVAL = 1000;
/**
* This agent sits between the WindowsTerminal class and provides a common interface for both conpty
* and winpty.
*/
var WindowsPtyAgent = /** @class */ (function () {
function WindowsPtyAgent(file, args, env, cwd, cols, rows, debug, _useConpty, conptyInheritCursor) {
var _this = this;
if (conptyInheritCursor === void 0) { conptyInheritCursor = false; }
this._useConpty = _useConpty;
this._pid = 0;
this._innerPid = 0;
if (this._useConpty === undefined || this._useConpty === true) {
this._useConpty = this._getWindowsBuildNumber() >= 18309;
}
if (this._useConpty) {
if (!conptyNative) {
try {
conptyNative = require('../build/Release/conpty.node');
}
catch (outerError) {
try {
conptyNative = require('../build/Debug/conpty.node');
}
catch (innerError) {
console.error('innerError', innerError);
// Re-throw the exception from the Release require if the Debug require fails as well
throw outerError;
}
}
}
}
else {
if (!winptyNative) {
try {
winptyNative = require('../build/Release/pty.node');
}
catch (outerError) {
try {
winptyNative = require('../build/Debug/pty.node');
}
catch (innerError) {
console.error('innerError', innerError);
// Re-throw the exception from the Release require if the Debug require fails as well
throw outerError;
}
}
}
}
this._ptyNative = this._useConpty ? conptyNative : winptyNative;
// Sanitize input variable.
cwd = path.resolve(cwd);
// Compose command line
var commandLine = argsToCommandLine(file, args);
// Open pty session.
var term;
if (this._useConpty) {
term = this._ptyNative.startProcess(file, cols, rows, debug, this._generatePipeName(), conptyInheritCursor);
}
else {
term = this._ptyNative.startProcess(file, commandLine, env, cwd, cols, rows, debug);
this._pid = term.pid;
this._innerPid = term.innerPid;
}
// Not available on windows.
this._fd = term.fd;
// Generated incremental number that has no real purpose besides using it
// as a terminal id.
this._pty = term.pty;
// Create terminal pipe IPC channel and forward to a local unix socket.
this._outSocket = new net_1.Socket();
this._outSocket.setEncoding('utf8');
// The conout socket must be ready out on another thread to avoid deadlocks
this._conoutSocketWorker = new windowsConoutConnection_1.ConoutConnection(term.conout);
this._conoutSocketWorker.onReady(function () {
_this._conoutSocketWorker.connectSocket(_this._outSocket);
});
this._outSocket.on('connect', function () {
_this._outSocket.emit('ready_datapipe');
});
var inSocketFD = fs.openSync(term.conin, 'w');
this._inSocket = new net_1.Socket({
fd: inSocketFD,
readable: false,
writable: true
});
this._inSocket.setEncoding('utf8');
if (this._useConpty) {
var connect = this._ptyNative.connect(this._pty, commandLine, cwd, env, function (c) { return _this._$onProcessExit(c); });
this._innerPid = connect.pid;
}
}
Object.defineProperty(WindowsPtyAgent.prototype, "inSocket", {
get: function () { return this._inSocket; },
enumerable: false,
configurable: true
});
Object.defineProperty(WindowsPtyAgent.prototype, "outSocket", {
get: function () { return this._outSocket; },
enumerable: false,
configurable: true
});
Object.defineProperty(WindowsPtyAgent.prototype, "fd", {
get: function () { return this._fd; },
enumerable: false,
configurable: true
});
Object.defineProperty(WindowsPtyAgent.prototype, "innerPid", {
get: function () { return this._innerPid; },
enumerable: false,
configurable: true
});
Object.defineProperty(WindowsPtyAgent.prototype, "pty", {
get: function () { return this._pty; },
enumerable: false,
configurable: true
});
WindowsPtyAgent.prototype.resize = function (cols, rows) {
if (this._useConpty) {
if (this._exitCode !== undefined) {
throw new Error('Cannot resize a pty that has already exited');
}
this._ptyNative.resize(this._pty, cols, rows);
return;
}
this._ptyNative.resize(this._pid, cols, rows);
};
WindowsPtyAgent.prototype.clear = function () {
if (this._useConpty) {
this._ptyNative.clear(this._pty);
}
};
WindowsPtyAgent.prototype.kill = function () {
var _this = this;
this._inSocket.readable = false;
this._outSocket.readable = false;
// Tell the agent to kill the pty, this releases handles to the process
if (this._useConpty) {
this._getConsoleProcessList().then(function (consoleProcessList) {
consoleProcessList.forEach(function (pid) {
try {
process.kill(pid);
}
catch (e) {
// Ignore if process cannot be found (kill ESRCH error)
}
});
_this._ptyNative.kill(_this._pty);
});
}
else {
// Because pty.kill closes the handle, it will kill most processes by itself.
// Process IDs can be reused as soon as all handles to them are
// dropped, so we want to immediately kill the entire console process list.
// If we do not force kill all processes here, node servers in particular
// seem to become detached and remain running (see
// Microsoft/vscode#26807).
var processList = this._ptyNative.getProcessList(this._pid);
this._ptyNative.kill(this._pid, this._innerPid);
processList.forEach(function (pid) {
try {
process.kill(pid);
}
catch (e) {
// Ignore if process cannot be found (kill ESRCH error)
}
});
}
this._conoutSocketWorker.dispose();
};
WindowsPtyAgent.prototype._getConsoleProcessList = function () {
var _this = this;
return new Promise(function (resolve) {
var agent = child_process_1.fork(path.join(__dirname, 'conpty_console_list_agent'), [_this._innerPid.toString()]);
agent.on('message', function (message) {
clearTimeout(timeout);
resolve(message.consoleProcessList);
});
var timeout = setTimeout(function () {
// Something went wrong, just send back the shell PID
agent.kill();
resolve([_this._innerPid]);
}, 5000);
});
};
Object.defineProperty(WindowsPtyAgent.prototype, "exitCode", {
get: function () {
if (this._useConpty) {
return this._exitCode;
}
var winptyExitCode = this._ptyNative.getExitCode(this._innerPid);
return winptyExitCode === -1 ? undefined : winptyExitCode;
},
enumerable: false,
configurable: true
});
WindowsPtyAgent.prototype._getWindowsBuildNumber = function () {
var osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release());
var buildNumber = 0;
if (osVersion && osVersion.length === 4) {
buildNumber = parseInt(osVersion[3]);
}
return buildNumber;
};
WindowsPtyAgent.prototype._generatePipeName = function () {
return "conpty-" + Math.random() * 10000000;
};
/**
* Triggered from the native side when a contpy process exits.
*/
WindowsPtyAgent.prototype._$onProcessExit = function (exitCode) {
var _this = this;
this._exitCode = exitCode;
this._flushDataAndCleanUp();
this._outSocket.on('data', function () { return _this._flushDataAndCleanUp(); });
};
WindowsPtyAgent.prototype._flushDataAndCleanUp = function () {
var _this = this;
if (this._closeTimeout) {
clearTimeout(this._closeTimeout);
}
this._closeTimeout = setTimeout(function () { return _this._cleanUpProcess(); }, FLUSH_DATA_INTERVAL);
};
WindowsPtyAgent.prototype._cleanUpProcess = function () {
this._inSocket.readable = false;
this._outSocket.readable = false;
this._outSocket.destroy();
};
return WindowsPtyAgent;
}());
exports.WindowsPtyAgent = WindowsPtyAgent;
// Convert argc/argv into a Win32 command-line following the escaping convention
// documented on MSDN (e.g. see CommandLineToArgvW documentation). Copied from
// winpty project.
function argsToCommandLine(file, args) {
if (isCommandLine(args)) {
if (args.length === 0) {
return file;
}
return argsToCommandLine(file, []) + " " + args;
}
var argv = [file];
Array.prototype.push.apply(argv, args);
var result = '';
for (var argIndex = 0; argIndex < argv.length; argIndex++) {
if (argIndex > 0) {
result += ' ';
}
var arg = argv[argIndex];
// if it is empty or it contains whitespace and is not already quoted
var hasLopsidedEnclosingQuote = xOr((arg[0] !== '"'), (arg[arg.length - 1] !== '"'));
var hasNoEnclosingQuotes = ((arg[0] !== '"') && (arg[arg.length - 1] !== '"'));
var quote = arg === '' ||
(arg.indexOf(' ') !== -1 ||
arg.indexOf('\t') !== -1) &&
((arg.length > 1) &&
(hasLopsidedEnclosingQuote || hasNoEnclosingQuotes));
if (quote) {
result += '\"';
}
var bsCount = 0;
for (var i = 0; i < arg.length; i++) {
var p = arg[i];
if (p === '\\') {
bsCount++;
}
else if (p === '"') {
result += repeatText('\\', bsCount * 2 + 1);
result += '"';
bsCount = 0;
}
else {
result += repeatText('\\', bsCount);
bsCount = 0;
result += p;
}
}
if (quote) {
result += repeatText('\\', bsCount * 2);
result += '\"';
}
else {
result += repeatText('\\', bsCount);
}
}
return result;
}
exports.argsToCommandLine = argsToCommandLine;
function isCommandLine(args) {
return typeof args === 'string';
}
function repeatText(text, count) {
var result = '';
for (var i = 0; i < count; i++) {
result += text;
}
return result;
}
function xOr(arg1, arg2) {
return ((arg1 && !arg2) || (!arg1 && arg2));
}

View File

@ -0,0 +1,200 @@
"use strict";
/**
* Copyright (c) 2012-2015, Christopher Jeffrey, Peter Sunde (MIT License)
* Copyright (c) 2016, Daniel Imms (MIT License).
* Copyright (c) 2018, Microsoft Corporation (MIT License).
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.WindowsTerminal = void 0;
var terminal_1 = require("./terminal");
var windowsPtyAgent_1 = require("./windowsPtyAgent");
var utils_1 = require("./utils");
var DEFAULT_FILE = 'cmd.exe';
var DEFAULT_NAME = 'Windows Shell';
var WindowsTerminal = /** @class */ (function (_super) {
__extends(WindowsTerminal, _super);
function WindowsTerminal(file, args, opt) {
var _this = _super.call(this, opt) || this;
_this._checkType('args', args, 'string', true);
// Initialize arguments
args = args || [];
file = file || DEFAULT_FILE;
opt = opt || {};
opt.env = opt.env || process.env;
if (opt.encoding) {
console.warn('Setting encoding on Windows is not supported');
}
var env = utils_1.assign({}, opt.env);
_this._cols = opt.cols || terminal_1.DEFAULT_COLS;
_this._rows = opt.rows || terminal_1.DEFAULT_ROWS;
var cwd = opt.cwd || process.cwd();
var name = opt.name || env.TERM || DEFAULT_NAME;
var parsedEnv = _this._parseEnv(env);
// If the terminal is ready
_this._isReady = false;
// Functions that need to run after `ready` event is emitted.
_this._deferreds = [];
// Create new termal.
_this._agent = new windowsPtyAgent_1.WindowsPtyAgent(file, args, parsedEnv, cwd, _this._cols, _this._rows, false, opt.useConpty, opt.conptyInheritCursor);
_this._socket = _this._agent.outSocket;
// Not available until `ready` event emitted.
_this._pid = _this._agent.innerPid;
_this._fd = _this._agent.fd;
_this._pty = _this._agent.pty;
// The forked windows terminal is not available until `ready` event is
// emitted.
_this._socket.on('ready_datapipe', function () {
// These events needs to be forwarded.
['connect', 'data', 'end', 'timeout', 'drain'].forEach(function (event) {
_this._socket.on(event, function () {
// Wait until the first data event is fired then we can run deferreds.
if (!_this._isReady && event === 'data') {
// Terminal is now ready and we can avoid having to defer method
// calls.
_this._isReady = true;
// Execute all deferred methods
_this._deferreds.forEach(function (fn) {
// NB! In order to ensure that `this` has all its references
// updated any variable that need to be available in `this` before
// the deferred is run has to be declared above this forEach
// statement.
fn.run();
});
// Reset
_this._deferreds = [];
}
});
});
// Shutdown if `error` event is emitted.
_this._socket.on('error', function (err) {
// Close terminal session.
_this._close();
// EIO, happens when someone closes our child process: the only process
// in the terminal.
// node < 0.6.14: errno 5
// node >= 0.6.14: read EIO
if (err.code) {
if (~err.code.indexOf('errno 5') || ~err.code.indexOf('EIO'))
return;
}
// Throw anything else.
if (_this.listeners('error').length < 2) {
throw err;
}
});
// Cleanup after the socket is closed.
_this._socket.on('close', function () {
_this.emit('exit', _this._agent.exitCode);
_this._close();
});
});
_this._file = file;
_this._name = name;
_this._readable = true;
_this._writable = true;
_this._forwardEvents();
return _this;
}
WindowsTerminal.prototype._write = function (data) {
this._defer(this._doWrite, data);
};
WindowsTerminal.prototype._doWrite = function (data) {
this._agent.inSocket.write(data);
};
/**
* openpty
*/
WindowsTerminal.open = function (options) {
throw new Error('open() not supported on windows, use Fork() instead.');
};
/**
* TTY
*/
WindowsTerminal.prototype.resize = function (cols, rows) {
var _this = this;
if (cols <= 0 || rows <= 0 || isNaN(cols) || isNaN(rows) || cols === Infinity || rows === Infinity) {
throw new Error('resizing must be done using positive cols and rows');
}
this._deferNoArgs(function () {
_this._agent.resize(cols, rows);
_this._cols = cols;
_this._rows = rows;
});
};
WindowsTerminal.prototype.clear = function () {
var _this = this;
this._deferNoArgs(function () {
_this._agent.clear();
});
};
WindowsTerminal.prototype.destroy = function () {
var _this = this;
this._deferNoArgs(function () {
_this.kill();
});
};
WindowsTerminal.prototype.kill = function (signal) {
var _this = this;
this._deferNoArgs(function () {
if (signal) {
throw new Error('Signals not supported on windows.');
}
_this._close();
_this._agent.kill();
});
};
WindowsTerminal.prototype._deferNoArgs = function (deferredFn) {
var _this = this;
// If the terminal is ready, execute.
if (this._isReady) {
deferredFn.call(this);
return;
}
// Queue until terminal is ready.
this._deferreds.push({
run: function () { return deferredFn.call(_this); }
});
};
WindowsTerminal.prototype._defer = function (deferredFn, arg) {
var _this = this;
// If the terminal is ready, execute.
if (this._isReady) {
deferredFn.call(this, arg);
return;
}
// Queue until terminal is ready.
this._deferreds.push({
run: function () { return deferredFn.call(_this, arg); }
});
};
Object.defineProperty(WindowsTerminal.prototype, "process", {
get: function () { return this._name; },
enumerable: false,
configurable: true
});
Object.defineProperty(WindowsTerminal.prototype, "master", {
get: function () { throw new Error('master is not supported on Windows'); },
enumerable: false,
configurable: true
});
Object.defineProperty(WindowsTerminal.prototype, "slave", {
get: function () { throw new Error('slave is not supported on Windows'); },
enumerable: false,
configurable: true
});
return WindowsTerminal;
}(terminal_1.Terminal));
exports.WindowsTerminal = WindowsTerminal;

View File

@ -0,0 +1,21 @@
"use strict";
/**
* Copyright (c) 2020, Microsoft Corporation (MIT License).
*/
Object.defineProperty(exports, "__esModule", { value: true });
var worker_threads_1 = require("worker_threads");
var net_1 = require("net");
var conout_1 = require("../shared/conout");
var conoutPipeName = worker_threads_1.workerData.conoutPipeName;
var conoutSocket = new net_1.Socket();
conoutSocket.setEncoding('utf8');
conoutSocket.connect(conoutPipeName, function () {
var server = net_1.createServer(function (workerSocket) {
conoutSocket.pipe(workerSocket);
});
server.listen(conout_1.getWorkerPipeName(conoutPipeName));
if (!worker_threads_1.parentPort) {
throw new Error('worker_threads parentPort is null');
}
worker_threads_1.parentPort.postMessage(1 /* READY */);
});