@nu-art/commando
Version:
151 lines (150 loc) • 6.07 kB
JavaScript
"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;