kui-shell
Version:
This is the monorepo for Kui, the hybrid command-line/GUI electron-based Kubernetes tool
144 lines • 5.38 kB
JavaScript
;
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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const debug_1 = require("debug");
const EventEmitter = require("events");
const server_1 = require("./server");
const channel_1 = require("./channel");
const debugE = debug_1.default('plugins/bash-like/pty/stdio-channel-proxy-stderr');
const debugW = debug_1.default('plugins/bash-like/pty/stdio-channel-proxy');
const debugK = debug_1.default('plugins/bash-like/pty/stdio-channel-kui');
const MARKER = '\n';
function heartbeat() {
debugW('heartbeat');
this.isAlive = true;
}
class StdioChannelWebsocketSide extends EventEmitter {
constructor(wss) {
super();
this.readyState = channel_1.ReadyState.CONNECTING;
this.wss = wss;
}
init(child, pollInterval = 30000) {
return __awaiter(this, void 0, void 0, function* () {
debugW('StdioChannelWebsocketSide.init');
this.wss.on('error', (err) => {
debugW('websocket error', err);
});
this.wss.on('connection', (ws) => {
debugW('got connection');
this.ws = ws;
ws.on('message', (data) => {
debugW('forwarding message downstream');
child.stdin.write(data);
});
ws.on('pong', heartbeat);
ws.on('close', () => {
debugW('killing child process, because client connection is dead');
child.kill();
});
});
const self = this;
setInterval(function ping() {
self.wss['clients'].forEach(function each(ws) {
if (ws.isAlive === false) {
debugW('killing child process, because client connection did not respond to ping');
child.kill();
return ws.terminate();
}
ws.isAlive = false;
ws.ping(() => {
});
});
}, pollInterval);
child.on('exit', (code) => {
debugW('child exit', code);
this.emit('exit', code);
});
child.stderr.on('data', (data) => {
if (data.length > 0) {
debugE(data.toString());
}
});
let pending;
child.stdout.on('data', (data) => {
const msg = data.toString();
if (!msg.endsWith(MARKER)) {
if (!pending) {
pending = msg;
}
else {
pending += msg;
}
}
else {
this.send(pending ? `${pending}${msg}` : msg);
pending = undefined;
}
});
});
}
close() {
debugW('closing stdio channel');
this.emit('exit');
}
send(msg) {
debugW('send', this.readyState === channel_1.ReadyState.OPEN);
if (msg === `open${MARKER}`) {
this.readyState = channel_1.ReadyState.OPEN;
this.emit('open');
}
else if (this.readyState === channel_1.ReadyState.OPEN) {
msg
.split(MARKER)
.filter(_ => _)
.forEach(_ => {
debugW('forwarding child output upstream');
this.ws.send(`${_}${MARKER}`);
});
}
}
removeEventListener(eventType, handler) {
this.off(eventType, handler);
}
}
exports.StdioChannelWebsocketSide = StdioChannelWebsocketSide;
class StdioChannelKuiSide extends EventEmitter {
constructor() {
super(...arguments);
this.readyState = channel_1.ReadyState.OPEN;
}
init(onExit) {
return __awaiter(this, void 0, void 0, function* () {
debugK('StdioChannelKuiSide.init');
yield server_1.onConnection(onExit)(this);
process.stdin.on('data', (data) => {
debugK('input', data.toString());
this.emit('message', data);
});
this.send('open');
});
}
close() {
debugW('closing stdio channel');
this.emit('close');
}
send(msg) {
if (this.readyState === channel_1.ReadyState.OPEN) {
process.stdout.write(`${msg}${MARKER}`);
}
}
removeEventListener(eventType, handler) {
this.off(eventType, handler);
}
}
exports.StdioChannelKuiSide = StdioChannelKuiSide;
//# sourceMappingURL=stdio-channel.js.map