compass-bot/scripts/tcp-repl-client.mjs
2023-11-05 21:49:15 +08:00

162 lines
3.9 KiB
JavaScript

import { Readable, Writable } from 'node:stream';
import { promisify } from 'node:util';
import net from 'node:net';
import { waitEvent } from 'compass-utils';
import readline from 'node:readline';
import yargs from 'yargs';
/**
* @param {Readable} input
* @param {Writable} output
*/
function ttyServer(input, output) {
const stdin = process.stdin;
const stdout = process.stdout;
stdin.setRawMode(true);
const writePromise = promisify((data, callback) => output.write(data, 'utf-8', callback));
async function sendData(data) {
if (!output.writable) {
return;
}
if (output.writableNeedDrain) {
await waitEvent(output, 'darin');
}
await writePromise(data);
}
stdin.on('data', async (chunk) => {
if (chunk.length === 1 && chunk[0] === 4) {
stdin.emit('end');
}
chunk = Buffer.concat([Buffer.from([0x02]), chunk]);
await sendData(chunk);
});
stdin.on('close', () => socket.destroy());
let init_data = Buffer.alloc(6);
init_data[0] = 0x01;
init_data.writeUInt16BE(stdout.rows, 1);
init_data.writeUInt16BE(stdout.columns, 3);
init_data.writeUInt8(stdout.getColorDepth(), 5);
sendData(init_data);
stdout.on('resize', () => {
let resize_data = Buffer.alloc(5);
resize_data[0] = 0x03;
resize_data.writeUInt16BE(stdout.rows, 1);
resize_data.writeUInt16BE(stdout.columns, 3);
sendData(resize_data);
});
stdout.on('drain', () => input.resume());
input.on('end', () => {
console.log('\nDisconnected (Server).');
process.exit(0);
});
stdin.on('end', () => {
input.destroy();
output.destroy();
console.log('\nDisconnected (Client).');
process.exit(0);
});
input.on('data', (data) => {
if (!(data instanceof Buffer)) {
data = Buffer.from(data);
}
if (data[0] === 0x02) {
if (!stdout.write(data)) { input.pause(); }
return;
}
if (data[0] == 0x10) {
// clearLine
let res = Buffer.alloc(17);
data.copy(res, 1, 1, 17);
res[0] = 0x04;
let dir = data.readInt8(17);
stdout.clearLine(dir, () => {
sendData(res);
});
} else if (data[0] == 0x11) {
// clearScreenDown
let res = Buffer.alloc(17);
data.copy(res, 1, 1, 17);
res[0] = 0x04;
stdout.clearScreenDown(() => {
sendData(res);
});
} else if (data[0] == 0x12) {
// cursorTo
let res = Buffer.alloc(17);
data.copy(res, 1, 1, 17);
res[0] = 0x04;
let flag = data[17];
let x = data.readUInt16BE(18);
let y = data.readUInt16BE(20);
if (flag) {
stdout.cursorTo(x, () => {
sendData(res);
});
} else {
stdout.cursorTo(x, y, () => {
sendData(res);
});
}
} else if (data[0] == 0x13) {
// moveCursor
let res = Buffer.alloc(17);
data.copy(res, 1, 1, 17);
res[0] = 0x04;
let dx = data.readUInt16BE(17);
let dy = data.readUInt16BE(19);
stdout.moveCursor(dx, dy, () => {
sendData(res);
});
}
});
}
async function main() {
const args = yargs((await import('yargs/helpers')).hideBin(process.argv))
.option('port', {
description: 'Remote TCP REPL service port.',
default: 2121,
}).usage('Uasge: hostname').help().alias('help', 'h').argv;
let socket = net.connect(args.port, args._[0]);
await waitEvent(socket, 'connect');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = promisify(rl.question).bind(rl);
const passcode_string = (await question('Passcode: ')).trim();
const passcode = Buffer.from(passcode_string, 'hex');
if (passcode.length == 0) {
console.log('A passcode is required.');
process.exit(1);
}
rl.close();
const sendPasscode = promisify((cb) => socket.write(passcode, cb));
await sendPasscode();
let res = await waitEvent(socket, 'data');
if (res[0] == 0x20) {
console.log('Passcode incorrect.');
process.exit(1);
}
console.log('Connected.');
process.stdin.resume();
ttyServer(socket, socket);
console.log(process.stdin.isRaw);
}
main().catch(err => {
console.error(`Error: ${err}`);
process.exit(1);
});