UNPKG

@nu-art/commando

Version:
151 lines (150 loc) 6.07 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InteractiveShell = void 0; const ts_common_1 = require("@nu-art/ts-common"); const node_child_process_1 = require("node:child_process"); const defaultLogLevelFilter = (log, std) => std === 'err' ? ts_common_1.LogLevel.Error : ts_common_1.LogLevel.Info; class InteractiveShell extends ts_common_1.Logger { /** * Constructs an InteractiveShell instance, initializes a detached shell session, and sets up log processors. */ constructor() { var _a, _b; super(); this._debug = false; this.logProcessors = []; this.logLevelFilter = defaultLogLevelFilter; /** * Executes a command in the interactive shell. * @param {string} command - The command to execute. */ this.execute = (command) => { var _a; if (this._debug) this.logDebug(`executing: `, `"""\n${command}\n"""`); (_a = this.shell.stdin) === null || _a === void 0 ? void 0 : _a.write(command + '\n', 'utf-8', (err) => { if (err) this.logError(`error`, err); }); }; /** * Awaits for the end of the interactive shell session. */ this.endInteractive = () => { return new Promise(resolve => { var _a; (_a = this.shell.stdin) === null || _a === void 0 ? void 0 : _a.end(resolve); }); }; /** * Sends a signal to terminate the shell process. * @param {NodeJS.Signals | number} [signal] - The signal to send to the shell process. * @returns {boolean | undefined} - The result of the kill operation. */ this.kill = (signal) => { if (!this.alive) return; this.logWarning(`Killing......`); // return this.gracefullyKill(this.shell.pid); return this.shell.emit('exit', 2); }; /** * Attempts to gracefully terminate the shell process. * @param {number} [pid] - Process ID of the shell to terminate. * @returns {Promise<void>} - Resolves when the shell process is gracefully killed. */ this.gracefullyKill = async (pid) => { if (!this.alive) return; return new Promise((resolve, reject) => { this.logWarning('Killing process'); this.shell.on('exit', async (code, signal) => { this.logWarning(`Process Killed ${signal}`); resolve(); }); if (pid) { this.logWarning(`KILLING PID: ${pid}`); process.kill(pid, 'SIGINT'); } else { this.logWarning(`KILLING SHELL WITH SIGINT`); this.shell.kill('SIGINT'); } }); }; this.shell = (0, node_child_process_1.spawn)('/bin/bash', { detached: true, // This is important to make the process a session leader shell: true }); this.alive = true; const printer = (std) => (data) => { var _a; const messages = data.toString().trim().split('\n'); if (!messages.length) return; for (const message of messages) { try { const toPrint = this.logProcessors.length === 0 || this.logProcessors.reduce((toPrint, processor) => { const filter = processor(message, std); return toPrint && filter; }, true); if (toPrint) { const logLevel = (_a = this.logLevelFilter(message, std)) !== null && _a !== void 0 ? _a : defaultLogLevelFilter(message, std); this.log(logLevel, false, [message]); } } catch (e) { this.logError(e); } } }; (_a = this.shell.stdout) === null || _a === void 0 ? void 0 : _a.on('data', printer('out')); (_b = this.shell.stderr) === null || _b === void 0 ? void 0 : _b.on('data', printer('err')); // Handle shell exit this.shell.on('close', (code) => { this.alive = false; this.logInfo(`child process exited with code ${code}`); }); } /** * Toggles or sets the debug mode. * @param {boolean} [debug] - If provided, sets the debug mode to this value. Otherwise, toggles the current debug mode. * @returns {boolean} - The current state of debug mode. */ debug(debug) { this._debug = debug !== null && debug !== void 0 ? debug : !this._debug; return this; } /** * Adds a log processor to handle log messages. * @param {(log: string, std: LogTypes) => boolean} processor - The log processor function. * @returns {this} - The InteractiveShell instance for method chaining. */ addLogProcessor(processor) { this.logProcessors.push(processor); return this; } setLogLevelFilter(logLevelFilter) { this.logLevelFilter = logLevelFilter; return this; } /** * Removes a log processor from handling log messages. * @param {(log: string, std: LogTypes) => boolean} processor - The log processor function to remove. * @returns {this} - The InteractiveShell instance for method chaining. */ removeLogProcessor(processor) { (0, ts_common_1.removeItemFromArray)(this.logProcessors, processor); return this; } /** * Sets a unique identifier for the shell session. * @param {string} uid - The unique identifier. * @returns {this} - The InteractiveShell instance for method chaining. */ setUID(uid) { this.setTag(uid); return this; } } exports.InteractiveShell = InteractiveShell;