UNPKG

@karinjs/node-pty

Version:

Use vite+tsup to recompile node-pty into esm, which is smaller in size.

908 lines (906 loc) 25.7 kB
import { createRequire } from "node:module"; import { fileURLToPath } from "node:url"; import { dirname } from "node:path"; const require = createRequire(import.meta.url); const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); import { EventEmitter } from "events"; import * as fs from "fs"; import * as os from "os"; import * as path from "path"; import { join } from "path"; import { Socket } from "net"; import { fork as fork$1 } from "child_process"; import { Worker } from "worker_threads"; import { C as ConoutWorkerMessage, g as getWorkerPipeName } from "./chunk/conout-DxH97NFz.js"; import * as tty from "tty"; function getAugmentedNamespace(n) { if (Object.prototype.hasOwnProperty.call(n, "__esModule")) return n; var f = n.default; if (typeof f == "function") { var a = function a2() { if (this instanceof a2) { return Reflect.construct(f, arguments, this.constructor); } return f.apply(this, arguments); }; a.prototype = f.prototype; } else a = {}; Object.defineProperty(a, "__esModule", { value: true }); Object.keys(n).forEach(function(k) { var d = Object.getOwnPropertyDescriptor(n, k); Object.defineProperty(a, k, d.get ? d : { enumerable: true, get: function() { return n[k]; } }); }); return a; } function commonjsRequire(path2) { return require("../build/Release/pty.node"); } class EventEmitter2 { constructor() { this._listeners = []; } get event() { if (!this._event) { this._event = (listener) => { this._listeners.push(listener); const disposable = { dispose: () => { for (let i = 0; i < this._listeners.length; i++) { if (this._listeners[i] === listener) { this._listeners.splice(i, 1); return; } } } }; return disposable; }; } return this._event; } fire(data) { const queue = []; for (let i = 0; i < this._listeners.length; i++) { queue.push(this._listeners[i]); } for (let i = 0; i < queue.length; i++) { queue[i].call(void 0, data); } } } const DEFAULT_COLS = 80; const DEFAULT_ROWS = 24; const FLOW_CONTROL_PAUSE = ""; const FLOW_CONTROL_RESUME = ""; class Terminal { constructor(opt) { this._pid = 0; this._fd = 0; this._cols = 0; this._rows = 0; this._readable = false; this._writable = false; this._onData = new EventEmitter2(); this._onExit = new EventEmitter2(); this._internalee = new EventEmitter(); this.handleFlowControl = !!opt?.handleFlowControl; this._flowControlPause = opt?.flowControlPause || FLOW_CONTROL_PAUSE; this._flowControlResume = opt?.flowControlResume || FLOW_CONTROL_RESUME; if (!opt) { return; } this._checkType("name", opt.name ? opt.name : void 0, "string"); this._checkType("cols", opt.cols ? opt.cols : void 0, "number"); this._checkType("rows", opt.rows ? opt.rows : void 0, "number"); this._checkType("cwd", opt.cwd ? opt.cwd : void 0, "string"); this._checkType("env", opt.env ? opt.env : void 0, "object"); this._checkType("uid", opt.uid ? opt.uid : void 0, "number"); this._checkType("gid", opt.gid ? opt.gid : void 0, "number"); this._checkType("encoding", opt.encoding ? opt.encoding : void 0, "string"); } get onData() { return this._onData.event; } get onExit() { return this._onExit.event; } get pid() { return this._pid; } get cols() { return this._cols; } get rows() { return this._rows; } write(data) { if (this.handleFlowControl) { if (data === this._flowControlPause) { this.pause(); return; } if (data === this._flowControlResume) { this.resume(); return; } } this._write(data); } _forwardEvents() { this.on("data", (e) => this._onData.fire(e)); this.on("exit", (exitCode, signal) => this._onExit.fire({ exitCode, signal })); } _checkType(name, value, type, allowArray = false) { if (value === void 0) { return; } if (allowArray) { if (Array.isArray(value)) { value.forEach((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 */ end(data) { this._socket.end(data); } /** See stream.Readable.pipe */ pipe(dest, options) { return this._socket.pipe(dest, options); } /** See net.Socket.pause */ pause() { return this._socket.pause(); } /** See net.Socket.resume */ resume() { return this._socket.resume(); } /** See net.Socket.setEncoding */ setEncoding(encoding) { if (this._socket._decoder) { delete this._socket._decoder; } if (encoding) { this._socket.setEncoding(encoding); } } addListener(eventName, listener) { this.on(eventName, listener); } on(eventName, listener) { if (eventName === "close") { this._internalee.on("close", listener); return; } this._socket.on(eventName, listener); } emit(eventName, ...args) { if (eventName === "close") { return this._internalee.emit.apply(this._internalee, arguments); } return this._socket.emit.apply(this._socket, arguments); } listeners(eventName) { return this._socket.listeners(eventName); } removeListener(eventName, listener) { this._socket.removeListener(eventName, listener); } removeAllListeners(eventName) { this._socket.removeAllListeners(eventName); } once(eventName, listener) { this._socket.once(eventName, listener); } _close() { this._socket.readable = false; this.write = () => { }; this.end = () => { }; this._writable = false; this._readable = false; } _parseEnv(env) { const keys = Object.keys(env || {}); const pairs = []; for (let i = 0; i < keys.length; i++) { if (keys[i] === void 0) { continue; } pairs.push(keys[i] + "=" + env[keys[i]]); } return pairs; } } const FLUSH_DATA_INTERVAL$1 = 1e3; class ConoutConnection { constructor(_conoutPipeName) { this._conoutPipeName = _conoutPipeName; this._isDisposed = false; this._onReady = new EventEmitter2(); const workerData = { conoutPipeName: _conoutPipeName }; const scriptPath = __dirname.replace("node_modules.asar", "node_modules.asar.unpacked"); this._worker = new Worker(join(scriptPath, "worker/conoutSocketWorker.js"), { workerData }); this._worker.on("message", (message) => { switch (message) { case ConoutWorkerMessage.READY: this._onReady.fire(); return; default: console.warn("Unexpected ConoutWorkerMessage", message); } }); } get onReady() { return this._onReady.event; } dispose() { if (this._isDisposed) { return; } this._isDisposed = true; this._drainDataAndClose(); } connectSocket(socket) { socket.connect(getWorkerPipeName(this._conoutPipeName)); } _drainDataAndClose() { if (this._drainTimeout) { clearTimeout(this._drainTimeout); } this._drainTimeout = setTimeout(() => this._destroySocket(), FLUSH_DATA_INTERVAL$1); } async _destroySocket() { await this._worker.terminate(); } } let conptyNative; let winptyNative; const FLUSH_DATA_INTERVAL = 1e3; class WindowsPtyAgent { constructor(file, args, env, cwd, cols, rows, debug, _useConpty, _useConptyDll = false, conptyInheritCursor = false) { this._useConpty = _useConpty; this._useConptyDll = _useConptyDll; this._pid = 0; this._innerPid = 0; if (this._useConpty === void 0 || 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); 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); throw outerError; } } } } this._ptyNative = this._useConpty ? conptyNative : winptyNative; cwd = path.resolve(cwd); const commandLine = argsToCommandLine(file, args); let term; if (this._useConpty) { term = this._ptyNative.startProcess(file, cols, rows, debug, this._generatePipeName(), conptyInheritCursor, this._useConptyDll); } else { term = this._ptyNative.startProcess(file, commandLine, env, cwd, cols, rows, debug); this._pid = term.pid; this._innerPid = term.innerPid; } this._fd = term.fd; this._pty = term.pty; this._outSocket = new Socket(); this._outSocket.setEncoding("utf8"); this._conoutSocketWorker = new ConoutConnection(term.conout); this._conoutSocketWorker.onReady(() => { this._conoutSocketWorker.connectSocket(this._outSocket); }); this._outSocket.on("connect", () => { this._outSocket.emit("ready_datapipe"); }); const inSocketFD = fs.openSync(term.conin, "w"); this._inSocket = new Socket({ fd: inSocketFD, readable: false, writable: true }); this._inSocket.setEncoding("utf8"); if (this._useConpty) { const connect = this._ptyNative.connect(this._pty, commandLine, cwd, env, (c) => this._$onProcessExit(c)); this._innerPid = connect.pid; } } get inSocket() { return this._inSocket; } get outSocket() { return this._outSocket; } get fd() { return this._fd; } get innerPid() { return this._innerPid; } get pty() { return this._pty; } resize(cols, rows) { if (this._useConpty) { if (this._exitCode !== void 0) { throw new Error("Cannot resize a pty that has already exited"); } this._ptyNative.resize(this._pty, cols, rows, this._useConptyDll); return; } this._ptyNative.resize(this._pid, cols, rows); } clear() { if (this._useConpty) { this._ptyNative.clear(this._pty, this._useConptyDll); } } kill() { this._inSocket.readable = false; this._outSocket.readable = false; if (this._useConpty) { this._getConsoleProcessList().then((consoleProcessList) => { consoleProcessList.forEach((pid) => { try { process.kill(pid); } catch (e) { } }); this._ptyNative.kill(this._pty, this._useConptyDll); }); } else { const processList = this._ptyNative.getProcessList(this._pid); this._ptyNative.kill(this._pid, this._innerPid); processList.forEach((pid) => { try { process.kill(pid); } catch (e) { } }); } this._conoutSocketWorker.dispose(); } _getConsoleProcessList() { return new Promise((resolve) => { const agent = fork$1(path.join(__dirname, "conpty_console_list_agent"), [this._innerPid.toString()]); agent.on("message", (message) => { clearTimeout(timeout); resolve(message.consoleProcessList); }); const timeout = setTimeout(() => { agent.kill(); resolve([this._innerPid]); }, 5e3); }); } get exitCode() { if (this._useConpty) { return this._exitCode; } const winptyExitCode = this._ptyNative.getExitCode(this._innerPid); return winptyExitCode === -1 ? void 0 : winptyExitCode; } _getWindowsBuildNumber() { const osVersion = /(\d+)\.(\d+)\.(\d+)/g.exec(os.release()); let buildNumber = 0; if (osVersion && osVersion.length === 4) { buildNumber = parseInt(osVersion[3]); } return buildNumber; } _generatePipeName() { return `conpty-${Math.random() * 1e7}`; } /** * Triggered from the native side when a contpy process exits. */ _$onProcessExit(exitCode) { this._exitCode = exitCode; this._flushDataAndCleanUp(); this._outSocket.on("data", () => this._flushDataAndCleanUp()); } _flushDataAndCleanUp() { if (this._closeTimeout) { clearTimeout(this._closeTimeout); } this._closeTimeout = setTimeout(() => this._cleanUpProcess(), FLUSH_DATA_INTERVAL); } _cleanUpProcess() { this._inSocket.readable = false; this._outSocket.readable = false; this._outSocket.destroy(); } } function argsToCommandLine(file, args) { if (isCommandLine(args)) { if (args.length === 0) { return file; } return `${argsToCommandLine(file, [])} ${args}`; } const argv = [file]; Array.prototype.push.apply(argv, args); let result = ""; for (let argIndex = 0; argIndex < argv.length; argIndex++) { if (argIndex > 0) { result += " "; } const arg = argv[argIndex]; const hasLopsidedEnclosingQuote = xOr(arg[0] !== '"', arg[arg.length - 1] !== '"'); const hasNoEnclosingQuotes = arg[0] !== '"' && arg[arg.length - 1] !== '"'; const quote = arg === "" || (arg.indexOf(" ") !== -1 || arg.indexOf(" ") !== -1) && (arg.length > 1 && (hasLopsidedEnclosingQuote || hasNoEnclosingQuotes)); if (quote) { result += '"'; } let bsCount = 0; for (let i = 0; i < arg.length; i++) { const 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; } function isCommandLine(args) { return typeof args === "string"; } function repeatText(text, count) { let result = ""; for (let i = 0; i < count; i++) { result += text; } return result; } function xOr(arg1, arg2) { return arg1 && !arg2 || !arg1 && arg2; } function assign(target, ...sources) { sources.forEach((source) => Object.keys(source).forEach((key) => target[key] = source[key])); return target; } const DEFAULT_FILE$1 = "cmd.exe"; const DEFAULT_NAME$1 = "Windows Shell"; class WindowsTerminal extends Terminal { constructor(file, args, opt) { super(opt); this._checkType("args", args, "string", true); args = args || []; file = file || DEFAULT_FILE$1; opt = opt || {}; opt.env = opt.env || process.env; if (opt.encoding) { console.warn("Setting encoding on Windows is not supported"); } const env = assign({}, opt.env); this._cols = opt.cols || DEFAULT_COLS; this._rows = opt.rows || DEFAULT_ROWS; const cwd = opt.cwd || process.cwd(); const name = opt.name || env.TERM || DEFAULT_NAME$1; const parsedEnv = this._parseEnv(env); this._isReady = false; this._deferreds = []; this._agent = new WindowsPtyAgent(file, args, parsedEnv, cwd, this._cols, this._rows, false, opt.useConpty, opt.useConptyDll, opt.conptyInheritCursor); this._socket = this._agent.outSocket; this._pid = this._agent.innerPid; this._fd = this._agent.fd; this._pty = this._agent.pty; this._socket.on("ready_datapipe", () => { ["connect", "data", "end", "timeout", "drain"].forEach((event) => { this._socket.on(event, () => { if (!this._isReady && event === "data") { this._isReady = true; this._deferreds.forEach((fn) => { fn.run(); }); this._deferreds = []; } }); }); this._socket.on("error", (err) => { this._close(); if (err.code) { if (~err.code.indexOf("errno 5") || ~err.code.indexOf("EIO")) return; } if (this.listeners("error").length < 2) { throw err; } }); this._socket.on("close", () => { this.emit("exit", this._agent.exitCode); this._close(); }); }); this._file = file; this._name = name; this._readable = true; this._writable = true; this._forwardEvents(); } _write(data) { this._defer(this._doWrite, data); } _doWrite(data) { this._agent.inSocket.write(data); } /** * openpty */ static open(options) { throw new Error("open() not supported on windows, use Fork() instead."); } /** * TTY */ resize(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"); } this._deferNoArgs(() => { this._agent.resize(cols, rows); this._cols = cols; this._rows = rows; }); } clear() { this._deferNoArgs(() => { this._agent.clear(); }); } destroy() { this._deferNoArgs(() => { this.kill(); }); } kill(signal) { this._deferNoArgs(() => { if (signal) { throw new Error("Signals not supported on windows."); } this._close(); this._agent.kill(); }); } _deferNoArgs(deferredFn) { if (this._isReady) { deferredFn.call(this); return; } this._deferreds.push({ run: () => deferredFn.call(this) }); } _defer(deferredFn, arg) { if (this._isReady) { deferredFn.call(this, arg); return; } this._deferreds.push({ run: () => deferredFn.call(this, arg) }); } get process() { return this._name; } get master() { throw new Error("master is not supported on Windows"); } get slave() { throw new Error("slave is not supported on Windows"); } } const windowsTerminal = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, WindowsTerminal }, Symbol.toStringTag, { value: "Module" })); const require$$0 = /* @__PURE__ */ getAugmentedNamespace(windowsTerminal); function prebuildName() { const tags = []; tags.push(process.versions.hasOwnProperty("electron") ? "electron" : "node"); tags.push("abi" + process.versions.modules); if (os.platform() === "linux" && fs.existsSync("/etc/alpine-release")) { tags.push("musl"); } return tags.join(".") + ".node"; } const pathToBuild = path.resolve(__dirname, `../prebuilds/${os.platform()}-${os.arch()}/${prebuildName()}`); const ptyPath = fs.existsSync(pathToBuild) ? pathToBuild : null; let pty; try { pty = commonjsRequire(ptyPath || "../build/Release/pty.node"); } catch (outerError) { try { pty = commonjsRequire(ptyPath ? "../build/Release/pty.node" : "../build/Debug/pty.node"); } catch (innerError) { console.error("innerError", innerError); throw outerError; } } const pty$1 = pty; let helperPath; helperPath = "../build/Release/spawn-helper"; helperPath = path.resolve(__dirname, helperPath); helperPath = helperPath.replace("app.asar", "app.asar.unpacked"); helperPath = helperPath.replace("node_modules.asar", "node_modules.asar.unpacked"); const DEFAULT_FILE = "sh"; const DEFAULT_NAME = "xterm"; const DESTROY_SOCKET_TIMEOUT_MS = 200; class UnixTerminal extends Terminal { constructor(file, args, opt) { super(opt); this._boundClose = false; this._emittedClose = false; if (typeof args === "string") { throw new Error("args as a string is not supported on unix."); } args = args || []; file = file || DEFAULT_FILE; opt = opt || {}; opt.env = opt.env || process.env; this._cols = opt.cols || DEFAULT_COLS; this._rows = opt.rows || DEFAULT_ROWS; const uid = opt.uid ?? -1; const gid = opt.gid ?? -1; const env = assign({}, opt.env); if (opt.env === process.env) { this._sanitizeEnv(env); } const cwd = opt.cwd || process.cwd(); env.PWD = cwd; const name = opt.name || env.TERM || DEFAULT_NAME; env.TERM = name; const parsedEnv = this._parseEnv(env); const encoding = opt.encoding === void 0 ? "utf8" : opt.encoding; const onexit = (code, signal) => { if (!this._emittedClose) { if (this._boundClose) { return; } this._boundClose = true; let timeout = setTimeout(() => { timeout = null; this._socket.destroy(); }, DESTROY_SOCKET_TIMEOUT_MS); this.once("close", () => { if (timeout !== null) { clearTimeout(timeout); } this.emit("exit", code, signal); }); return; } this.emit("exit", code, signal); }; const term = pty$1.fork(file, args, parsedEnv, cwd, this._cols, this._rows, uid, gid, encoding === "utf8", helperPath, onexit); this._socket = new tty.ReadStream(term.fd); if (encoding !== null) { this._socket.setEncoding(encoding); } this._socket.on("error", (err) => { if (err.code) { if (~err.code.indexOf("EAGAIN")) { return; } } this._close(); if (!this._emittedClose) { this._emittedClose = true; this.emit("close"); } if (err.code) { if (~err.code.indexOf("errno 5") || ~err.code.indexOf("EIO")) { return; } } 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", () => { if (this._emittedClose) { return; } this._emittedClose = true; this._close(); this.emit("close"); }); this._forwardEvents(); } get master() { return this._master; } get slave() { return this._slave; } _write(data) { this._socket.write(data); } /* Accessors */ get fd() { return this._fd; } get ptsName() { return this._pty; } /** * openpty */ static open(opt) { const self = Object.create(UnixTerminal.prototype); opt = opt || {}; if (arguments.length > 1) { opt = { cols: arguments[1], rows: arguments[2] }; } const cols = opt.cols || DEFAULT_COLS; const rows = opt.rows || DEFAULT_ROWS; const encoding = opt.encoding === void 0 ? "utf8" : opt.encoding; const term = pty$1.open(cols, rows); self._master = new tty.ReadStream(term.master); if (encoding !== null) { self._master.setEncoding(encoding); } self._master.resume(); self._slave = new tty.ReadStream(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", (err) => { self._close(); if (self.listeners("error").length < 2) { throw err; } }); self._socket.on("close", () => { self._close(); }); return self; } destroy() { this._close(); this._socket.once("close", () => { this.kill("SIGHUP"); }); this._socket.destroy(); } kill(signal) { try { process.kill(this.pid, signal || "SIGHUP"); } catch (e) { } } /** * Gets the name of the process. */ get process() { if (process.platform === "darwin") { const title = pty$1.process(this._fd); return title !== "kernel_task" ? title : this._file; } return pty$1.process(this._fd, this._pty) || this._file; } /** * TTY */ resize(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$1.resize(this._fd, cols, rows); this._cols = cols; this._rows = rows; } clear() { } _sanitizeEnv(env) { delete env["TMUX"]; delete env["TMUX_PANE"]; delete env["STY"]; delete env["WINDOW"]; delete env["WINDOWID"]; delete env["TERMCAP"]; delete env["COLUMNS"]; delete env["LINES"]; } } const unixTerminal = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, UnixTerminal }, Symbol.toStringTag, { value: "Module" })); const require$$1 = /* @__PURE__ */ getAugmentedNamespace(unixTerminal); let terminalCtor; if (process.platform === "win32") { terminalCtor = require$$0.WindowsTerminal; } else { terminalCtor = require$$1.UnixTerminal; } function spawn(file, args, opt) { return new terminalCtor(file, args, opt); } function fork(file, args, opt) { return new terminalCtor(file, args, opt); } function createTerminal(file, args, opt) { return new terminalCtor(file, args, opt); } function open(options) { return terminalCtor.open(options); } const native = process.platform !== "win32" ? commonjsRequire(ptyPath || "../build/Release/pty.node") : null; export { createTerminal, fork, native, open, spawn };