vibetunnel/web/node-pty/lib/windowsTerminal.js
2025-07-11 08:23:47 +02:00

200 lines
7.1 KiB
JavaScript

"use strict";
/**
* Simplified Windows terminal implementation without threading
* Removed ConoutSocketWorker and shared pipe architecture
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.WindowsTerminal = void 0;
const fs = __importStar(require("fs"));
const net_1 = require("net");
const terminal_1 = require("./terminal");
const utils_1 = require("./utils");
const DEFAULT_FILE = 'cmd.exe';
const DEFAULT_NAME = 'Windows Shell';
let conptyNative;
class WindowsTerminal extends terminal_1.Terminal {
get master() { return this._outSocket; }
get slave() { return this._inSocket; }
constructor(file, args, opt) {
super(opt);
this._isReady = false;
this._pid = 0;
this._innerPid = 0;
this._useConptyDll = false;
// Load native module
if (!conptyNative) {
try {
conptyNative = require('../build/Release/conpty.node');
}
catch (outerError) {
try {
conptyNative = require('../build/Debug/conpty.node');
}
catch (innerError) {
throw outerError;
}
}
}
this._ptyNative = conptyNative;
// Initialize arguments
args = args || [];
file = file || DEFAULT_FILE;
opt = opt || {};
opt.env = opt.env || process.env;
const env = (0, utils_1.assign)({}, opt.env);
this._cols = opt.cols || terminal_1.DEFAULT_COLS;
this._rows = opt.rows || terminal_1.DEFAULT_ROWS;
const cwd = opt.cwd || process.cwd();
const parsedEnv = this._parseEnv(env);
// Compose command line
const commandLine = this._argsToCommandLine(file, args);
// Start ConPTY process
const pipeName = this._generatePipeName();
const term = this._ptyNative.startProcess(file, this._cols, this._rows, false, pipeName, false, this._useConptyDll);
this._pty = term.pty;
this._fd = term.fd;
// Create direct socket connections without worker threads
this._setupDirectSockets(term);
// Connect the process
const connect = this._ptyNative.connect(this._pty, commandLine, cwd, parsedEnv, this._useConptyDll, (exitCode) => {
this._exitCode = exitCode;
this.emit('exit', exitCode);
this._close();
});
this._innerPid = connect.pid;
this._pid = connect.pid;
this._file = file;
this._name = opt.name || env.TERM || DEFAULT_NAME;
this._readable = true;
this._writable = true;
this._forwardEvents();
}
_setupDirectSockets(term) {
// Setup output socket - read directly from conout
const outFd = fs.openSync(term.conout, 'r');
this._outSocket = new net_1.Socket({ fd: outFd, readable: true, writable: false });
this._outSocket.setEncoding('utf8');
this._socket = this._outSocket;
// Setup input socket - write directly to conin
const inFd = fs.openSync(term.conin, 'w');
this._inSocket = new net_1.Socket({ fd: inFd, readable: false, writable: true });
this._inSocket.setEncoding('utf8');
// Forward events directly
this._outSocket.on('data', (data) => {
if (!this._isReady) {
this._isReady = true;
}
this.emit('data', data);
});
this._outSocket.on('error', (err) => {
if (err.code && (err.code.includes('EPIPE') || err.code.includes('EIO'))) {
// Expected errors when process exits
return;
}
this.emit('error', err);
});
this._outSocket.on('close', () => {
if (this._exitCode === undefined) {
this.emit('exit', 0);
}
this._close();
});
}
_write(data) {
if (this._inSocket && this._inSocket.writable) {
this._inSocket.write(data);
}
}
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');
}
if (this._exitCode !== undefined) {
throw new Error('Cannot resize a pty that has already exited');
}
this._cols = cols;
this._rows = rows;
this._ptyNative.resize(this._pty, cols, rows, this._useConptyDll);
}
clear() {
this._ptyNative.clear(this._pty, this._useConptyDll);
}
kill(signal) {
this._close();
try {
process.kill(this._pid);
}
catch (e) {
// Ignore if process cannot be found
}
this._ptyNative.kill(this._pty, this._useConptyDll);
}
_close() {
if (this._inSocket) {
this._inSocket.destroy();
}
if (this._outSocket) {
this._outSocket.destroy();
}
}
_generatePipeName() {
return `\\\\.\\pipe\\conpty-${Date.now()}-${Math.random()}`;
}
_argsToCommandLine(file, args) {
if (typeof args === 'string') {
return `${file} ${args}`;
}
const argv = [file];
if (args) {
argv.push(...args);
}
return argv.map(arg => {
if (arg.includes(' ') || arg.includes('\t')) {
return `"${arg.replace(/"/g, '\\"')}"`;
}
return arg;
}).join(' ');
}
static open(options) {
throw new Error('open() not supported on windows, use spawn() instead.');
}
get process() { return this._name; }
get pid() { return this._pid; }
destroy() {
this.kill();
}
}
exports.WindowsTerminal = WindowsTerminal;