const { EventEmitter } = require('events'); const { exec } = require('child_process'); const defaultShell = require('default-shell'); let spawn; try { spawn = require('child_pty').spawn; } catch (err) { console.error( 'A native module failed to load. Typically this means ' + 'you installed the modules incorrectly.\n Use `scripts/install.sh` ' + 'to trigger the installation.\n ' + 'More information: https://github.com/zeit/hyperterm/issues/72' ); } const TITLE_POLL_INTERVAL = 500; module.exports = class Session extends EventEmitter { constructor ({ rows, cols: columns }) { super(); this.pty = spawn(defaultShell, ['--login'], { columns, rows, cwd: process.env.HOME, env: Object.assign({}, process.env, { TERM: 'xterm-256color' }) }); this.pty.stdout.on('data', (data) => { this.emit('data', data.toString('utf8')); }); this.pty.on('exit', () => { if (!this.ended) { this.ended = true; this.emit('exit'); } }); this.getTitle(); } focus () { this.getTitle(true); } blur () { clearTimeout(this.titlePoll); } getTitle (subscribe = false) { if ('win32' === process.platform) return; let tty = this.pty.stdout.ttyname; tty = tty.replace(/^\/dev\/tty/, ''); // try to exclude grep from the results // by grepping for `[s]001` instead of `s001` tty = `[${tty[0]}]${tty.substr(1)}`; // TODO: limit the concurrency of how many processes we run? // TODO: only tested on mac exec(`ps uxac | grep ${tty} | head -n 1`, (err, out) => { if (this.ended) return; if (err) return; let title = out.split(' ').pop(); if (title) { title = title.replace(/^\(/, ''); title = title.replace(/\)?\n$/, ''); if (title !== this.lastTitle) { this.emit('title', title); this.lastTitle = title; } } if (subscribe) { this.titlePoll = setTimeout(() => this.getTitle(true), TITLE_POLL_INTERVAL); } }); } exit () { this.destroy(); } write (data) { this.pty.stdin.write(data); } resize ({ cols: columns, rows }) { try { this.pty.stdout.resize({ columns, rows }); } catch (err) { console.error(err.stack); } } destroy () { try { this.pty.kill('SIGHUP'); } catch (err) { console.error('exit error', err.stack); } this.emit('exit'); this.ended = true; clearTimeout(this.titlePoll); } };